Skip to content

Commit 9e40a80

Browse files
Feat: Added Private section for ied
1 parent 03279a6 commit 9e40a80

File tree

16 files changed

+373
-11
lines changed

16 files changed

+373
-11
lines changed

public/js/init.js

Whitespace-only changes.
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import {
2+
css,
3+
customElement,
4+
html,
5+
LitElement,
6+
property,
7+
TemplateResult,
8+
} from 'lit-element';
9+
import { classMap } from 'lit-html/directives/class-map';
10+
11+
import '@material/mwc-icon';
12+
import { nothing } from 'lit-html';
13+
14+
function closestTo<E extends Element>(node: Node, selector: string): E | null {
15+
const closest =
16+
node.nodeType === Node.ELEMENT_NODE
17+
? (<Element>node).closest<E>(selector)
18+
: null;
19+
20+
if (closest) return closest;
21+
22+
const root = <Document | DocumentFragment>node.getRootNode();
23+
24+
if (root instanceof ShadowRoot) return closestTo(root.host, selector);
25+
26+
return null;
27+
}
28+
29+
/**
30+
* A responsive container rendering actions in a header.
31+
*
32+
* The "action" slot may contain up to eight icon buttons.
33+
* The "icon" slot, if filled overrides the icon property.
34+
* The default slot will be rendered into the pane body in a single column.
35+
*/
36+
@customElement('action-pane')
37+
export class ActionPane extends LitElement {
38+
/** caption text, displayed in the header */
39+
@property({ type: String })
40+
label?: string;
41+
/** icon name, displayed unless the "icon" slot is filled */
42+
@property({ type: String })
43+
icon?: string;
44+
/** color header with secondary theme color while focus is within */
45+
@property({ type: Boolean })
46+
secondary = false;
47+
/** highlight pane with dotted outline */
48+
@property({ type: Boolean })
49+
highlighted = false;
50+
/** nesting level, default (closest pane ancestor's level) + 1 */
51+
@property({ type: Number })
52+
level = 1;
53+
54+
async firstUpdated(): Promise<void> {
55+
this.tabIndex = 0;
56+
57+
const parentPane = closestTo<ActionPane>(this.parentNode!, 'action-pane');
58+
if (parentPane) this.level = parentPane.level + 1;
59+
60+
this.level = Math.floor(this.level);
61+
}
62+
63+
private renderHeader(): TemplateResult {
64+
const content = html`<span
65+
><slot name="icon"
66+
>${this.icon
67+
? html`<mwc-icon>${this.icon}</mwc-icon>`
68+
: nothing}</slot
69+
></span
70+
>
71+
${this.label ?? nothing}
72+
<nav><slot name="action"></slot></nav>`;
73+
74+
const headingLevel = Math.floor(Math.max(this.level, 1));
75+
// Sometimes a TemplateResult is passed in as Label, not a string. So only when it's a string show a title.
76+
const title = typeof this.label === 'string' ? this.label : '';
77+
switch (headingLevel) {
78+
case 1:
79+
return html`<h1 title="${title}">${content}</h1>`;
80+
case 2:
81+
return html`<h2 title="${title}">${content}</h2>`;
82+
case 3:
83+
return html`<h3 title="${title}">${content}</h3>`;
84+
default:
85+
return html`<h4 title="${title}">${content}</h4>`;
86+
}
87+
}
88+
89+
render(): TemplateResult {
90+
return html`<section
91+
class="${classMap({
92+
secondary: this.secondary,
93+
highlighted: this.highlighted,
94+
contrasted: this.level % 2 === 0,
95+
})}"
96+
>
97+
${this.renderHeader()}
98+
<div><slot></slot></div>
99+
</section>`;
100+
}
101+
102+
static styles = css`
103+
:host {
104+
outline: none;
105+
}
106+
107+
:host(:focus-within) section {
108+
box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14),
109+
0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2);
110+
outline-width: 4px;
111+
transition: all 250ms linear;
112+
}
113+
114+
section {
115+
background-color: var(--mdc-theme-surface);
116+
transition: all 200ms linear;
117+
outline-style: solid;
118+
margin: 0px;
119+
outline-width: 0px;
120+
outline-color: var(--mdc-theme-primary);
121+
}
122+
123+
section.secondary {
124+
outline-color: var(--mdc-theme-secondary);
125+
}
126+
127+
section > div {
128+
display: flex;
129+
flex-direction: column;
130+
gap: 12px;
131+
padding: 8px 12px 16px;
132+
clear: right;
133+
}
134+
135+
.highlighted {
136+
outline-style: dotted;
137+
outline-width: 2px;
138+
}
139+
140+
:host(:focus-within) .highlighted {
141+
outline-style: solid;
142+
}
143+
144+
.contrasted {
145+
background-color: var(--mdc-theme-on-primary);
146+
}
147+
148+
h1,
149+
h2,
150+
h3,
151+
h4 {
152+
color: var(--mdc-theme-on-surface);
153+
font-family: 'Roboto', sans-serif;
154+
font-weight: 300;
155+
overflow: clip visible;
156+
white-space: nowrap;
157+
text-overflow: ellipsis;
158+
margin: 0px;
159+
line-height: 52px;
160+
padding-left: 0.3em;
161+
}
162+
163+
nav {
164+
float: right;
165+
}
166+
167+
mwc-icon {
168+
vertical-align: middle;
169+
position: relative;
170+
top: -0.1em;
171+
--mdc-icon-size: 1em;
172+
}
173+
174+
::slotted([slot='icon']) {
175+
vertical-align: middle;
176+
position: relative;
177+
top: -0.1em;
178+
--mdc-icon-size: 1em;
179+
}
180+
`;
181+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './action-pane.js';

src/editors/sitipe/foundation.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@ export const selectors = <Record<SubstationTag, string>>(
1313
export const SIEMENS_SITIPE_IED_REF = 'Siemens-SITIPE-IEDRef';
1414

1515
export const SIEMENS_SITIPE_BAY_TEMPLATE = 'Siemens-SITIPE-BayTemplate';
16+
17+
export const SIEMENS_SITIPE_IED_TEMPLATE_REF = 'Siemens-SITIPE-IEDTemplateRef';

src/editors/sitipe/sitipe-bay.ts

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,16 @@ import '../../action-icon.js';
3232
import {
3333
SIEMENS_SITIPE_IED_REF,
3434
SIEMENS_SITIPE_BAY_TEMPLATE,
35+
SIEMENS_SITIPE_IED_TEMPLATE_REF,
3536
} from './foundation.js';
3637

3738
import {
3839
BayTypical,
40+
BTComponent,
3941
getBayTypicalComponents,
4042
getImportedBTComponentData,
4143
getImportedBtComponents,
44+
ImportedBTComponent,
4245
} from './sitipe-service.js';
4346
import { defaultNamingStrategy, NamingStrategy } from './sitipe-substation.js';
4447
import { get } from 'lit-translate';
@@ -461,6 +464,7 @@ export class SitipeBay extends LitElement {
461464
actions: [],
462465
title: 'Sitipe',
463466
};
467+
464468
const bayTypicalElement: Element = createElement(this.doc, 'Private', {
465469
type: SIEMENS_SITIPE_BAY_TEMPLATE,
466470
});
@@ -497,7 +501,9 @@ export class SitipeBay extends LitElement {
497501
'application/xml'
498502
);
499503

500-
this.prepareImport(doc, iedName);
504+
if (this.isValidDoc(doc)) {
505+
this.prepareImport(doc, iedName, btComponent);
506+
}
501507
});
502508
});
503509
});
@@ -506,15 +512,15 @@ export class SitipeBay extends LitElement {
506512
});
507513
}
508514

509-
public prepareImport(doc: Document, iedName: string): void {
515+
private isValidDoc(doc: Document): boolean {
510516
if (!doc) {
511517
this.dispatchEvent(
512518
newLogEvent({
513519
kind: 'error',
514520
title: get('import.log.loaderror'),
515521
})
516522
);
517-
return;
523+
return false;
518524
}
519525

520526
if (doc.querySelector('parsererror')) {
@@ -524,11 +530,23 @@ export class SitipeBay extends LitElement {
524530
title: get('import.log.parsererror'),
525531
})
526532
);
527-
return;
533+
return false;
528534
}
529535

530-
const ieds = Array.from(doc.querySelectorAll(':root > IED'));
531-
if (ieds.length === 0) {
536+
return true;
537+
}
538+
539+
private getIeds(doc: Document): Element[] {
540+
return Array.from(doc.querySelectorAll(':root > IED'));
541+
}
542+
543+
protected prepareImport(
544+
doc: Document,
545+
iedName: string,
546+
btComponent: BTComponent
547+
): void {
548+
const ieds: Element[] = this.getIeds(doc);
549+
if (!ieds.length) {
532550
this.dispatchEvent(
533551
newLogEvent({
534552
kind: 'error',
@@ -537,15 +555,51 @@ export class SitipeBay extends LitElement {
537555
);
538556
return;
539557
}
540-
541-
if (ieds.length === 1) {
542-
this.importIED(ieds[0], iedName);
558+
if (ieds.length > 1) {
543559
return;
544560
}
545-
}
546561

547-
private importIED(ied: Element, iedName: string): void {
562+
const ied: Element = ieds[0];
563+
564+
const oldIEDName: string = ied.getAttribute('name') || '';
548565
ied.setAttribute('name', iedName);
566+
567+
this.importIED(ied);
568+
569+
if (iedName || oldIEDName) {
570+
console.log('just before createelement');
571+
console.log('doc element: ', this.doc.documentElement);
572+
573+
const privateIEDRef: Element = createElement(this.doc, 'Private', {
574+
type: SIEMENS_SITIPE_IED_TEMPLATE_REF,
575+
});
576+
privateIEDRef.textContent = btComponent.name || oldIEDName;
577+
578+
console.log(
579+
'Adding private iedRef element',
580+
privateIEDRef,
581+
'to ied: ',
582+
ied
583+
);
584+
585+
this.dispatchEvent(
586+
newActionEvent({
587+
title: get('editing.import', { name: ied.getAttribute('name')! }),
588+
actions: [
589+
{
590+
new: {
591+
parent: ied,
592+
element: privateIEDRef,
593+
},
594+
},
595+
],
596+
})
597+
);
598+
}
599+
return;
600+
}
601+
602+
private importIED(ied: Element): void {
549603
if (!isIedNameUnique(ied, this.doc)) {
550604
this.dispatchEvent(
551605
newLogEvent({

src/translations/de.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,7 @@ export const de: Translations = {
689689
loaderror: 'Datei kann nicht geladen werden',
690690
importerror: 'IED Element kann nicht importiert werden',
691691
missingied: 'Kein IED Element in der Datei',
692+
multipleied: 'Mehrere IED-Elemente in einer Datei',
692693
nouniqueied: 'IED Element {{ name }} bereits geladen',
693694
},
694695
},

src/translations/en.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,7 @@ export const en = {
688688
loaderror: 'Could not load file',
689689
importerror: 'Could not import IED',
690690
missingied: 'No IED element in the file',
691+
multipleied: 'Multiple IED elements found',
691692
nouniqueied: 'IED element {{ name }} already in the file',
692693
},
693694
},

src/wizards/bayTypical.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { html, TemplateResult } from 'lit-html';
2+
import { get } from 'lit-translate';
3+
import { getBayTypicals } from '../editors/sitipe/sitipe-service.js';
4+
import { Wizard } from '../foundation.js';
5+
6+
function render(): TemplateResult {
7+
getBayTypicals().then(res => {
8+
console.log('res', res);
9+
});
10+
return html`<span>Test</span>`;
11+
}
12+
export function bayTypicalWizard(): Wizard {
13+
return [
14+
{
15+
title: get('voltagelevel.wizard.title.edit'),
16+
content: [render()],
17+
},
18+
];
19+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Browser, launch, Page } from 'puppeteer';
2+
import { CucumberWorld } from '../support/cucumber-world';
3+
4+
export class BrowserDelegate {
5+
6+
browser!: Browser;
7+
page!: Page;
8+
9+
constructor(private world: CucumberWorld) {
10+
}
11+
12+
async init(): Promise<void> {
13+
this.browser = await launch({
14+
headless: false
15+
});
16+
this.page = (await this.browser.pages())[0];
17+
}
18+
19+
async destroy(): Promise<void> {
20+
await this.browser.close();
21+
}
22+
23+
async navigate(): Promise<void> {
24+
25+
}
26+
27+
}

0 commit comments

Comments
 (0)