Skip to content

Commit e5bc0b8

Browse files
danyillTamás Russ
andauthored
feat(editors/ied): Improve IED editor UI for IED and LN selection (openscd#1288)
feat(editors/ied): Improve IED editor UI for IED and LN selection, closes openscd#1287 --------- Co-authored-by: Tamás Russ <[email protected]>
1 parent 2ae1cd3 commit e5bc0b8

File tree

3 files changed

+120
-29
lines changed

3 files changed

+120
-29
lines changed

packages/open-scd/src/editors/IED.ts

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export default class IedPlugin extends LitElement {
3232
/** The document being edited as provided to plugins by [[`OpenSCD`]]. */
3333
@property()
3434
doc!: XMLDocument;
35+
3536
@property({ type: Number })
3637
editCount = -1;
3738

@@ -69,6 +70,12 @@ export default class IedPlugin extends LitElement {
6970
uniqueLNClassList.push(lnClass);
7071
return true;
7172
})
73+
.sort((a, b) => {
74+
const aLnClass = a.getAttribute('lnClass') ?? '';
75+
const bLnClass = b.getAttribute('lnClass') ?? '';
76+
77+
return aLnClass.localeCompare(bLnClass);
78+
})
7279
.map(element => {
7380
const lnClass = element.getAttribute('lnClass');
7481
const label = this.nsdoc.getDataDescription(element).label;
@@ -92,17 +99,28 @@ export default class IedPlugin extends LitElement {
9299
return undefined;
93100
}
94101

102+
lNClassListOpenedOnce = false;
103+
95104
protected updated(_changedProperties: PropertyValues): void {
96105
super.updated(_changedProperties);
97106

98-
// When the document is updated, we reset the selected IED.
99-
if (
107+
// When the document is updated, we reset the selected IED if it no longer exists
108+
const isDocumentUpdated =
100109
_changedProperties.has('doc') ||
101110
_changedProperties.has('editCount') ||
102-
_changedProperties.has('nsdoc')
103-
) {
111+
_changedProperties.has('nsdoc');
112+
113+
if (isDocumentUpdated) {
114+
// if the IED exists, retain selection
115+
const iedExists = this.doc?.querySelector(
116+
`IED[name="${this.selectedIEDs[0]}"]`
117+
);
118+
119+
if (iedExists) return;
120+
104121
this.selectedIEDs = [];
105122
this.selectedLNClasses = [];
123+
this.lNClassListOpenedOnce = false;
106124

107125
const iedList = this.iedList;
108126
if (iedList.length > 0) {
@@ -111,12 +129,22 @@ export default class IedPlugin extends LitElement {
111129
this.selectedIEDs = [iedName];
112130
}
113131
}
114-
this.selectedLNClasses = this.lnClassList.map(
115-
lnClassInfo => lnClassInfo[0]
116-
);
117132
}
118133
}
119134

135+
private calcSelectedLNClasses(): string[] {
136+
const somethingSelected = this.selectedLNClasses.length > 0;
137+
const lnClasses = this.lnClassList.map( lnClassInfo => lnClassInfo[0] );
138+
139+
let selectedLNClasses = lnClasses;
140+
141+
if(somethingSelected){
142+
selectedLNClasses = lnClasses.filter( lnClass => this.selectedLNClasses.includes(lnClass));
143+
}
144+
145+
return selectedLNClasses;
146+
}
147+
120148
render(): TemplateResult {
121149
const iedList = this.iedList;
122150
if (iedList.length > 0) {
@@ -129,10 +157,25 @@ export default class IedPlugin extends LitElement {
129157
icon="developer_board"
130158
.header=${translate('iededitor.iedSelector')}
131159
@selected-items-changed="${(e: SelectedItemsChangedEvent) => {
132-
this.selectedIEDs = e.detail.selectedItems;
133-
this.selectedLNClasses = this.lnClassList.map(
134-
lnClassInfo => lnClassInfo[0]
160+
const equalArrays = <T>(first: T[], second: T[]): boolean => {
161+
return (
162+
first.length === second.length &&
163+
first.every((val, index) => val === second[index])
164+
);
165+
};
166+
167+
const selectionChanged = !equalArrays(
168+
this.selectedIEDs,
169+
e.detail.selectedItems
135170
);
171+
172+
if (!selectionChanged) {
173+
return;
174+
}
175+
176+
this.lNClassListOpenedOnce = false;
177+
this.selectedIEDs = e.detail.selectedItems;
178+
this.selectedLNClasses = [];
136179
this.requestUpdate('selectedIed');
137180
}}"
138181
>
@@ -160,6 +203,7 @@ export default class IedPlugin extends LitElement {
160203
multi="true"
161204
.header="${translate('iededitor.lnFilter')}"
162205
@selected-items-changed="${(e: SelectedItemsChangedEvent) => {
206+
163207
this.selectedLNClasses = e.detail.selectedItems;
164208
this.requestUpdate('selectedIed');
165209
}}"
@@ -184,7 +228,7 @@ export default class IedPlugin extends LitElement {
184228
.editCount=${this.editCount}
185229
.doc=${this.doc}
186230
.element=${this.selectedIed}
187-
.selectedLNClasses=${this.selectedLNClasses}
231+
.selectedLNClasses=${this.calcSelectedLNClasses()}
188232
.nsdoc=${this.nsdoc}
189233
></ied-container>
190234
</section>`;

packages/open-scd/test/integration/editors/IED.test.ts

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,14 +145,66 @@ describe('IED Plugin', () => {
145145
).length
146146
).to.eql(5);
147147

148-
await deselectLNClasses('CSWI');
148+
await selectLNClasses('CSWI');
149149
await new Promise(resolve => setTimeout(resolve, 100)); // await animation
150150

151151
expect(
152152
getLDeviceContainer(getIedContainer()).shadowRoot!.querySelectorAll(
153153
'ln-container'
154154
).length
155-
).to.eql(3);
155+
).to.eql(2);
156+
});
157+
158+
it('when other IED selected, all LNs are selected by default', async () => {
159+
await selectLNClasses('XCBR');
160+
161+
await selectIed('IED3');
162+
await new Promise(resolve => setTimeout(resolve, 100)); // await animation
163+
164+
expect(
165+
element.shadowRoot?.querySelectorAll('ied-container').length
166+
).to.eql(1);
167+
expect(
168+
getIedContainer().shadowRoot?.querySelector('action-pane')!.shadowRoot
169+
?.innerHTML
170+
).to.include('IED3');
171+
172+
expect(
173+
getLDeviceContainer(getIedContainer()).shadowRoot!.querySelectorAll(
174+
'ln-container'
175+
).length
176+
).to.eql(9);
177+
});
178+
179+
it('when filtering LNs, if none are selected, all are selected', async () => {
180+
await selectIed('IED3');
181+
182+
const oscdFilterButton = <FilterButton>(
183+
element.shadowRoot!.querySelector(
184+
'oscd-filter-button[id="lnClassesFilter"]'
185+
)
186+
);
187+
const filterButton = <LitElement>(
188+
oscdFilterButton!.shadowRoot!.querySelector('mwc-icon-button')
189+
);
190+
filterButton.click();
191+
await element.updateComplete;
192+
193+
const primaryButton = <HTMLElement>(
194+
oscdFilterButton!.shadowRoot!.querySelector(
195+
'mwc-button[slot="primaryAction"]'
196+
)
197+
);
198+
primaryButton.click();
199+
await element.updateComplete;
200+
201+
await new Promise(resolve => setTimeout(resolve, 100)); // await animation
202+
203+
expect(
204+
getLDeviceContainer(getIedContainer()).shadowRoot!.querySelectorAll(
205+
'ln-container'
206+
).length
207+
).to.eql(9);
156208
});
157209

158210
it('then renders the path of elements correctly', async () => {
@@ -277,7 +329,7 @@ describe('IED Plugin', () => {
277329
);
278330
}
279331

280-
async function deselectLNClasses(lnClass: string): Promise<void> {
332+
async function selectLNClasses(lnClass: string): Promise<void> {
281333
const oscdFilterButton = <FilterButton>(
282334
element.shadowRoot!.querySelector(
283335
'oscd-filter-button[id="lnClassesFilter"]'

packages/open-scd/test/integration/editors/__snapshots__/IED.test.snap.js

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -116,51 +116,46 @@ snapshots["IED Plugin with a doc loaded containing IEDs looks like the latest sn
116116
aria-disabled="false"
117117
graphic="control"
118118
mwc-list-item=""
119-
selected=""
120119
tabindex="0"
121-
value="LLN0"
120+
value="CILO"
122121
>
123-
LLN0
122+
CILO
124123
</mwc-check-list-item>
125124
<mwc-check-list-item
126125
aria-disabled="false"
127126
graphic="control"
128127
mwc-list-item=""
129-
selected=""
130128
tabindex="-1"
131-
value="XCBR"
129+
value="CSWI"
132130
>
133-
XCBR
131+
CSWI
134132
</mwc-check-list-item>
135133
<mwc-check-list-item
136134
aria-disabled="false"
137135
graphic="control"
138136
mwc-list-item=""
139-
selected=""
140137
tabindex="-1"
141-
value="CSWI"
138+
value="LLN0"
142139
>
143-
CSWI
140+
LLN0
144141
</mwc-check-list-item>
145142
<mwc-check-list-item
146143
aria-disabled="false"
147144
graphic="control"
148145
mwc-list-item=""
149-
selected=""
150146
tabindex="-1"
151-
value="XSWI"
147+
value="XCBR"
152148
>
153-
XSWI
149+
XCBR
154150
</mwc-check-list-item>
155151
<mwc-check-list-item
156152
aria-disabled="false"
157153
graphic="control"
158154
mwc-list-item=""
159-
selected=""
160155
tabindex="-1"
161-
value="CILO"
156+
value="XSWI"
162157
>
163-
CILO
158+
XSWI
164159
</mwc-check-list-item>
165160
</oscd-filter-button>
166161
<element-path class="elementPath">

0 commit comments

Comments
 (0)