Skip to content

Commit 6b1add6

Browse files
authored
Merge pull request #11 from com-pas/feat/4-add-function-in-sld
feat: Add function in SLD
2 parents a52118a + dc2d6e2 commit 6b1add6

File tree

13 files changed

+1249
-23
lines changed

13 files changed

+1249
-23
lines changed

__snapshots__/scl-bay-template.spec.snap.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,8 @@ snapshots['SclBayTemplate Plugin looks like the latest snapshot'] = `<main>
154154
>
155155
</mwc-button>
156156
</mwc-dialog>
157+
<add-function-dialog-632c87ac id="add-function-dialog">
158+
</add-function-dialog-632c87ac>
157159
<mwc-dialog
158160
heading="LNodeType Library Info"
159161
id="lnode-lib-info"
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { expect, fixture, html } from '@open-wc/testing';
2+
import { Insert } from '@openscd/open-scd-core';
3+
import AddFunctionDialog, { DialogMode } from './add-function-dialog.js';
4+
import { addFunctionTestfile } from '../testfiles/add-function.testfile.js';
5+
6+
describe('AddFunctionDialog', () => {
7+
let element: AddFunctionDialog;
8+
9+
let doc: XMLDocument;
10+
let voltageLevel: Element;
11+
let bay: Element;
12+
let conductingEquipment: Element;
13+
let powerTransformer: Element;
14+
15+
beforeEach(async () => {
16+
doc = new DOMParser().parseFromString(
17+
addFunctionTestfile,
18+
'application/xml'
19+
);
20+
voltageLevel = doc.querySelector('VoltageLevel[name="vl1"]')!;
21+
bay = doc.querySelector('Bay[name="bay1"]')!;
22+
conductingEquipment = doc.querySelector('ConductingEquipment[name="CE1"]')!;
23+
powerTransformer = doc.querySelector('PowerTransformer[name="PT1"]')!;
24+
25+
element = await fixture(
26+
html`<add-function-dialog-632c87ac></add-function-dialog-632c87ac>`
27+
);
28+
29+
await element.updateComplete;
30+
});
31+
32+
it('should add function to parent bay for conducting equipment and add powersystemrelation', () => {
33+
let insert: Insert;
34+
element.addEventListener('oscd-edit', e => {
35+
insert = e.detail as Insert;
36+
});
37+
38+
element.show(conductingEquipment, DialogMode.ConductingEquipment);
39+
40+
const name = 'func1';
41+
const desc = 'desc1';
42+
const type = 'type1';
43+
element.nameTextField.value = name;
44+
element.descTextField.value = desc;
45+
element.typeTextField.value = type;
46+
47+
(element as any).onSave();
48+
49+
// eslint-disable-next-line no-unused-expressions
50+
expect(insert!).to.not.be.null;
51+
52+
const { parent, node: functionElement } = insert!;
53+
expect(parent).to.equal(bay);
54+
expect((functionElement as Element).getAttribute('name')).to.equal(name);
55+
expect((functionElement as Element).getAttribute('desc')).to.equal(desc);
56+
expect((functionElement as Element).getAttribute('type')).to.equal(type);
57+
58+
const powerSystemRelation = (functionElement as Element).querySelector(
59+
'PowerSystemRelation'
60+
);
61+
62+
// eslint-disable-next-line no-unused-expressions
63+
expect(powerSystemRelation).to.not.be.null;
64+
expect(powerSystemRelation?.getAttribute('relation')).to.equal(
65+
'sub1/vl1/bay1/CE1'
66+
);
67+
});
68+
69+
it('should add function to parent bay, voltagelevel or substation for powertransformer and add powersystemrelation', () => {
70+
let insert: Insert;
71+
element.addEventListener('oscd-edit', e => {
72+
insert = e.detail as Insert;
73+
});
74+
75+
element.show(powerTransformer, DialogMode.PowerTransformer);
76+
77+
const name = 'func1';
78+
const desc = 'desc1';
79+
const type = 'type1';
80+
element.nameTextField.value = name;
81+
element.descTextField.value = desc;
82+
element.typeTextField.value = type;
83+
84+
(element as any).onSave();
85+
86+
// eslint-disable-next-line no-unused-expressions
87+
expect(insert!).to.not.be.null;
88+
89+
const { parent, node: functionElement } = insert!;
90+
expect(parent).to.equal(voltageLevel);
91+
expect((functionElement as Element).getAttribute('name')).to.equal(name);
92+
expect((functionElement as Element).getAttribute('desc')).to.equal(desc);
93+
expect((functionElement as Element).getAttribute('type')).to.equal(type);
94+
95+
const powerSystemRelation = (functionElement as Element).querySelector(
96+
'PowerSystemRelation'
97+
);
98+
99+
// eslint-disable-next-line no-unused-expressions
100+
expect(powerSystemRelation).to.not.be.null;
101+
expect(powerSystemRelation?.getAttribute('relation')).to.equal(
102+
'sub1/vl1/PT1'
103+
);
104+
});
105+
});

components/add-function-dialog.ts

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
import { css, html, LitElement } from 'lit';
2+
import { customElement, query } from 'lit/decorators.js';
3+
4+
import { Insert, newEditEvent } from '@openscd/open-scd-core';
5+
6+
import { SclTextField } from '@openenergytools/scl-text-field';
7+
import { createElement } from '@openenergytools/scl-lib/dist/foundation/utils.js';
8+
9+
import { Dialog } from '@material/mwc-dialog';
10+
import { Button } from '@material/mwc-button';
11+
import { ScopedElementsMixin } from '@open-wc/scoped-elements/lit-element.js';
12+
import {
13+
createPowerSystemRelationElement,
14+
getProcessPath,
15+
} from '../foundation/scl.js';
16+
17+
// eslint-disable-next-line no-shadow
18+
export enum DialogMode {
19+
PowerTransformer = 'PowerTransformer',
20+
ConductingEquipment = 'ConductingEquipment',
21+
}
22+
23+
interface FormValue {
24+
name: string | null;
25+
desc: string | null;
26+
type: string | null;
27+
}
28+
29+
@customElement('add-function-dialog-632c87ac')
30+
export default class AddFunctionDialog extends ScopedElementsMixin(LitElement) {
31+
static scopedElements = {
32+
'scl-text-field': SclTextField,
33+
'mwc-dialog': Dialog,
34+
'mwc-button': Button,
35+
};
36+
37+
@query('#add-function-dialog-internal') dialog!: Dialog;
38+
39+
@query('scl-text-field[label="name"]') nameTextField!: SclTextField;
40+
41+
@query('scl-text-field[label="desc"]') descTextField!: SclTextField;
42+
43+
@query('scl-text-field[label="type"]') typeTextField!: SclTextField;
44+
45+
private element: Element | null = null;
46+
47+
private mode: DialogMode = DialogMode.ConductingEquipment;
48+
49+
public show(element: Element, mode: DialogMode) {
50+
this.element = element;
51+
this.mode = mode;
52+
this.dialog.show();
53+
}
54+
55+
public close() {
56+
this.resetForm();
57+
this.dialog.close();
58+
}
59+
60+
private resetForm(): void {
61+
this.nameTextField.value = '';
62+
this.descTextField.value = '';
63+
this.typeTextField.value = '';
64+
}
65+
66+
private onSave(): void {
67+
const name = this.nameTextField.value;
68+
const desc = this.descTextField.value;
69+
const type = this.typeTextField.value;
70+
71+
const values: FormValue = { name, desc, type };
72+
73+
if (this.element === null) {
74+
throw new Error(`No element provided to add function dialog`);
75+
}
76+
77+
if (this.mode === DialogMode.PowerTransformer) {
78+
// Add function to parent bay, VoltageLevel or substation
79+
// Add PowerSystemRelation -> PowerTransformer
80+
81+
const bayVoltageLevelOrSubstation = this.element.closest(
82+
'Bay, VoltageLevel, Substation'
83+
);
84+
85+
if (bayVoltageLevelOrSubstation === null) {
86+
throw new Error(
87+
`Element ${this.element.getAttribute(
88+
'name'
89+
)} does not have parent bay, voltage level or substation`
90+
);
91+
}
92+
93+
const isPowerTransformer = this.element.tagName === 'PowerTransformer';
94+
const powerTransformer = isPowerTransformer
95+
? this.element
96+
: this.element.parentElement;
97+
98+
if (!powerTransformer) {
99+
throw new Error(`Powertransformer not found`);
100+
}
101+
102+
const pathToPowerTransformer = getProcessPath(powerTransformer);
103+
const powerSystemRelationElement = createPowerSystemRelationElement(
104+
this.element.ownerDocument,
105+
pathToPowerTransformer
106+
);
107+
108+
const functionElement = createElement(
109+
bayVoltageLevelOrSubstation.ownerDocument,
110+
'Function',
111+
{
112+
...values,
113+
}
114+
);
115+
116+
functionElement.appendChild(powerSystemRelationElement);
117+
118+
const functionInsert: Insert = {
119+
parent: bayVoltageLevelOrSubstation,
120+
node: functionElement,
121+
reference: null,
122+
};
123+
124+
this.dispatchEvent(newEditEvent(functionInsert));
125+
} else {
126+
// Add function to parent bay
127+
// Add PowerSystemRelation -> ConductingEquipment
128+
129+
const conductingEquipment = this.element;
130+
const bay = conductingEquipment.closest('Bay');
131+
132+
if (bay === null) {
133+
throw new Error(
134+
`ConductingEquipment ${conductingEquipment.getAttribute(
135+
'name'
136+
)} does not have parent bay`
137+
);
138+
}
139+
140+
const functionElement = createElement(bay.ownerDocument, 'Function', {
141+
...values,
142+
});
143+
144+
const pathToConductingEquipment = getProcessPath(conductingEquipment);
145+
const powerSystemRelationElement = createPowerSystemRelationElement(
146+
this.element.ownerDocument,
147+
pathToConductingEquipment
148+
);
149+
150+
functionElement.appendChild(powerSystemRelationElement);
151+
152+
const functionInsert: Insert = {
153+
parent: bay,
154+
node: functionElement,
155+
reference: null,
156+
};
157+
158+
this.dispatchEvent(newEditEvent(functionInsert));
159+
}
160+
this.close();
161+
}
162+
163+
protected render() {
164+
return html`<mwc-dialog
165+
id="add-function-dialog-internal"
166+
heading="Add function"
167+
>
168+
<div class="input-container">
169+
<scl-text-field
170+
label="name"
171+
.value=${''}
172+
required
173+
dialogInitialFocus
174+
></scl-text-field>
175+
<scl-text-field
176+
label="desc"
177+
.maybeValue=${''}
178+
nullable
179+
></scl-text-field>
180+
<scl-text-field
181+
label="type"
182+
.maybeValue=${''}
183+
nullable
184+
></scl-text-field>
185+
</div>
186+
<mwc-button
187+
slot="secondaryAction"
188+
label="close"
189+
@click=${this.close}
190+
></mwc-button>
191+
<mwc-button
192+
slot="primaryAction"
193+
label="save"
194+
icon="save"
195+
@click="${this.onSave}"
196+
></mwc-button>
197+
</mwc-dialog>`;
198+
}
199+
200+
static styles = css`
201+
.input-container {
202+
display: flex;
203+
flex-direction: column;
204+
gap: 16px;
205+
}
206+
207+
* {
208+
--mdc-theme-primary: var(--fedit-primary);
209+
--mdc-theme-secondary: var(--fedit-secondary);
210+
211+
--md-sys-color-primary: var(--fedit-primary);
212+
--md-sys-color-secondary: var(--fedit-secondary);
213+
--md-sys-typescale-body-large-font: var(
214+
--oscd-theme-text-font,
215+
'Roboto',
216+
sans-serif
217+
);
218+
--md-filled-text-field-input-text-color: var(--fedit-text-color);
219+
220+
--md-sys-color-surface: var(--fedit-surface);
221+
--md-sys-color-on-surface: var(--fedit-text-color);
222+
--md-sys-color-on-primary: var(--fedit-text-color);
223+
--md-sys-color-on-surface-variant: var(--fedit-text-color);
224+
--md-menu-container-color: var(--fedit-surface);
225+
font-family: var(--oscd-theme-text-font, 'Roboto', sans-serif);
226+
--md-sys-color-surface-container-highest: var(--fedit-surface);
227+
}
228+
`;
229+
}

0 commit comments

Comments
 (0)