Skip to content

Commit 011eec8

Browse files
refactor(editors/publisher): reset selections with missing reference (openscd#924)
1 parent b25f9a6 commit 011eec8

10 files changed

+354
-67
lines changed

src/editors/publisher/data-set-editor.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,34 +20,34 @@ import '../../filtered-list.js';
2020
import { FilteredList } from '../../filtered-list.js';
2121

2222
import { compareNames, identity, selector } from '../../foundation.js';
23-
import { styles } from './foundation.js';
23+
import { styles, updateElementReference } from './foundation.js';
2424

2525
@customElement('data-set-editor')
2626
export class DataSetEditor extends LitElement {
2727
/** The document being edited as provided to plugins by [[`OpenSCD`]]. */
2828
@property({ attribute: false })
29-
set doc(newDoc: XMLDocument) {
30-
if (this._doc === newDoc) return;
31-
32-
this.selectedDataSet = undefined;
33-
if (this.selectionList && this.selectionList.selected)
34-
(this.selectionList.selected as ListItem).selected = false;
35-
36-
this._doc = newDoc;
37-
38-
this.requestUpdate();
39-
}
40-
get doc(): XMLDocument {
41-
return this._doc;
42-
}
43-
private _doc!: XMLDocument;
29+
doc!: XMLDocument;
4430

4531
@state()
4632
selectedDataSet?: Element;
4733

4834
@query('.selectionlist') selectionList!: FilteredList;
4935
@query('mwc-button') selectDataSetButton!: Button;
5036

37+
/** Resets selected GOOSE, if not existing in new doc */
38+
update(props: Map<string | number | symbol, unknown>): void {
39+
if (props.has('doc') && this.selectedDataSet) {
40+
const newDataSet = updateElementReference(this.doc, this.selectedDataSet);
41+
42+
this.selectedDataSet = newDataSet ?? undefined;
43+
44+
if (!newDataSet && this.selectionList && this.selectionList.selected)
45+
(this.selectionList.selected as ListItem).selected = false;
46+
}
47+
48+
super.update(props);
49+
}
50+
5151
private selectDataSet(evt: Event): void {
5252
const id = ((evt.target as FilteredList).selected as ListItem).value;
5353
const dataSet = this.doc.querySelector(selector('DataSet', id));

src/editors/publisher/foundation.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
import { css } from 'lit-element';
22

3+
import { identity, selector } from '../../foundation.js';
4+
5+
export function updateElementReference(
6+
newDoc: XMLDocument,
7+
oldElement: Element
8+
): Element | null {
9+
if (!oldElement || !oldElement.closest('SCL')) return null;
10+
11+
const id = identity(oldElement);
12+
const newElement = newDoc.querySelector(selector(oldElement.tagName, id));
13+
14+
return newElement;
15+
}
16+
317
export const styles = css`
418
.content {
519
display: flex;

src/editors/publisher/gse-control-editor.ts

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,28 +22,13 @@ import { FilteredList } from '../../filtered-list.js';
2222

2323
import { gooseIcon } from '../../icons/icons.js';
2424
import { compareNames, identity, selector } from '../../foundation.js';
25-
import { styles } from './foundation.js';
25+
import { styles, updateElementReference } from './foundation.js';
2626

2727
@customElement('gse-control-editor')
2828
export class GseControlEditor extends LitElement {
2929
/** The document being edited as provided to plugins by [[`OpenSCD`]]. */
3030
@property({ attribute: false })
31-
set doc(newDoc: XMLDocument) {
32-
if (this._doc === newDoc) return;
33-
34-
this.selectedDataSet = undefined;
35-
this.selectedGseControl = undefined;
36-
if (this.selectionList && this.selectionList.selected)
37-
(this.selectionList.selected as ListItem).selected = false;
38-
39-
this._doc = newDoc;
40-
41-
this.requestUpdate();
42-
}
43-
get doc(): XMLDocument {
44-
return this._doc;
45-
}
46-
private _doc!: XMLDocument;
31+
doc!: XMLDocument;
4732

4833
@state()
4934
selectedGseControl?: Element;
@@ -53,6 +38,26 @@ export class GseControlEditor extends LitElement {
5338
@query('.selectionlist') selectionList!: FilteredList;
5439
@query('mwc-button') selectGSEControlButton!: Button;
5540

41+
/** Resets selected GOOSE and its DataSet, if not existing in new doc */
42+
update(props: Map<string | number | symbol, unknown>): void {
43+
if (props.has('doc') && this.selectedGseControl) {
44+
const newGseControl = updateElementReference(
45+
this.doc,
46+
this.selectedGseControl
47+
);
48+
49+
this.selectedGseControl = newGseControl ?? undefined;
50+
this.selectedDataSet = this.selectedGseControl
51+
? updateElementReference(this.doc, this.selectedDataSet!)
52+
: undefined;
53+
54+
if (!newGseControl && this.selectionList && this.selectionList.selected)
55+
(this.selectionList.selected as ListItem).selected = false;
56+
}
57+
58+
super.update(props);
59+
}
60+
5661
private selectGSEControl(evt: Event): void {
5762
const id = ((evt.target as FilteredList).selected as ListItem).value;
5863
const gseControl = this.doc.querySelector(selector('GSEControl', id));

src/editors/publisher/report-control-editor.ts

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,28 +22,13 @@ import { FilteredList } from '../../filtered-list.js';
2222

2323
import { compareNames, identity, selector } from '../../foundation.js';
2424
import { reportIcon } from '../../icons/icons.js';
25-
import { styles } from './foundation.js';
25+
import { styles, updateElementReference } from './foundation.js';
2626

2727
@customElement('report-control-editor')
2828
export class ReportControlEditor extends LitElement {
2929
/** The document being edited as provided to plugins by [[`OpenSCD`]]. */
3030
@property({ attribute: false })
31-
set doc(newDoc: XMLDocument) {
32-
if (this._doc === newDoc) return;
33-
34-
this.selectedDataSet = undefined;
35-
this.selectedReportControl = undefined;
36-
if (this.selectionList && this.selectionList.selected)
37-
(this.selectionList.selected as ListItem).selected = false;
38-
39-
this._doc = newDoc;
40-
41-
this.requestUpdate();
42-
}
43-
get doc(): XMLDocument {
44-
return this._doc;
45-
}
46-
private _doc!: XMLDocument;
31+
doc!: XMLDocument;
4732

4833
@state()
4934
selectedReportControl?: Element;
@@ -53,6 +38,30 @@ export class ReportControlEditor extends LitElement {
5338
@query('.selectionlist') selectionList!: FilteredList;
5439
@query('mwc-button') selectReportControlButton!: Button;
5540

41+
/** Resets selected Report and its DataSet, if not existing in new doc */
42+
update(props: Map<string | number | symbol, unknown>): void {
43+
if (props.has('doc') && this.selectedReportControl) {
44+
const newReportControl = updateElementReference(
45+
this.doc,
46+
this.selectedReportControl
47+
);
48+
49+
this.selectedReportControl = newReportControl ?? undefined;
50+
this.selectedDataSet = this.selectedReportControl
51+
? updateElementReference(this.doc, this.selectedDataSet!)
52+
: undefined;
53+
54+
if (
55+
!newReportControl &&
56+
this.selectionList &&
57+
this.selectionList.selected
58+
)
59+
(this.selectionList.selected as ListItem).selected = false;
60+
}
61+
62+
super.update(props);
63+
}
64+
5665
private selectReportControl(evt: Event): void {
5766
const id = ((evt.target as FilteredList).selected as ListItem).value;
5867
const reportControl = this.doc.querySelector(selector('ReportControl', id));

src/editors/publisher/sampled-value-control-editor.ts

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,28 +22,13 @@ import { FilteredList } from '../../filtered-list.js';
2222

2323
import { compareNames, identity, selector } from '../../foundation.js';
2424
import { smvIcon } from '../../icons/icons.js';
25-
import { styles } from './foundation.js';
25+
import { styles, updateElementReference } from './foundation.js';
2626

2727
@customElement('sampled-value-control-editor')
2828
export class SampledValueControlEditor extends LitElement {
2929
/** The document being edited as provided to plugins by [[`OpenSCD`]]. */
3030
@property({ attribute: false })
31-
set doc(newDoc: XMLDocument) {
32-
if (this._doc === newDoc) return;
33-
34-
this.selectedDataSet = undefined;
35-
this.selectedSampledValueControl = undefined;
36-
if (this.selectionList && this.selectionList.selected)
37-
(this.selectionList.selected as ListItem).selected = false;
38-
39-
this._doc = newDoc;
40-
41-
this.requestUpdate();
42-
}
43-
get doc(): XMLDocument {
44-
return this._doc;
45-
}
46-
private _doc!: XMLDocument;
31+
doc!: XMLDocument;
4732

4833
@state()
4934
selectedSampledValueControl?: Element;
@@ -53,6 +38,30 @@ export class SampledValueControlEditor extends LitElement {
5338
@query('.selectionlist') selectionList!: FilteredList;
5439
@query('mwc-button') selectSampledValueControlButton!: Button;
5540

41+
/** Resets selected SMV and its DataSet, if not existing in new doc */
42+
update(props: Map<string | number | symbol, unknown>): void {
43+
if (props.has('doc') && this.selectedSampledValueControl) {
44+
const newSampledValueControl = updateElementReference(
45+
this.doc,
46+
this.selectedSampledValueControl
47+
);
48+
49+
this.selectedSampledValueControl = newSampledValueControl ?? undefined;
50+
this.selectedDataSet = this.selectedSampledValueControl
51+
? updateElementReference(this.doc, this.selectedDataSet!)
52+
: undefined;
53+
54+
if (
55+
!newSampledValueControl &&
56+
this.selectionList &&
57+
this.selectionList.selected
58+
)
59+
(this.selectionList.selected as ListItem).selected = false;
60+
}
61+
62+
super.update(props);
63+
}
64+
5665
private selectSMVControl(evt: Event): void {
5766
const id = ((evt.target as FilteredList).selected as ListItem).value;
5867
const smvControl = this.doc.querySelector(

test/unit/editors/publisher/data-set-editor.test.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,22 @@ import { expect, fixture, html } from '@open-wc/testing';
22

33
import '../../../../src/editors/publisher/data-set-editor.js';
44
import { DataSetEditor } from '../../../../src/editors/publisher/data-set-editor.js';
5+
import { cloneTestDoc } from './foundation.test.js';
56

67
describe('Editor for DataSet element', () => {
78
let doc: XMLDocument;
9+
let otherDoc: XMLDocument;
810
let element: DataSetEditor;
911

1012
beforeEach(async () => {
1113
doc = await fetch('/test/testfiles/valid2007B4.scd')
1214
.then(response => response.text())
1315
.then(str => new DOMParser().parseFromString(str, 'application/xml'));
1416

17+
otherDoc = await fetch('/test/testfiles/history.scd')
18+
.then(response => response.text())
19+
.then(str => new DOMParser().parseFromString(str, 'application/xml'));
20+
1521
element = await fixture(
1622
html`<data-set-editor .doc=${doc}></data-set-editor>`
1723
);
@@ -21,6 +27,8 @@ describe('Editor for DataSet element', () => {
2127
await expect(element).shadowDom.to.equalSnapshot());
2228

2329
describe('with a selected DataSet', () => {
30+
let newDoc: XMLDocument;
31+
2432
beforeEach(async () => {
2533
(
2634
element.shadowRoot?.querySelector(
@@ -33,5 +41,56 @@ describe('Editor for DataSet element', () => {
3341

3442
it('looks like the latest snapshot', async () =>
3543
await expect(element).shadowDom.to.equalSnapshot());
44+
45+
describe('on selected element update', () => {
46+
beforeEach(async () => {
47+
newDoc = cloneTestDoc(doc);
48+
element.doc = newDoc;
49+
await element.updateComplete;
50+
51+
await element.selectionList.requestUpdate();
52+
});
53+
54+
it('does not reset selected Element', async () =>
55+
expect(element.selectedDataSet).to.equal(
56+
newDoc.querySelector('DataSet')
57+
));
58+
59+
it('does not reset selection', async () =>
60+
expect(element.selectionList.selected).to.not.be.null);
61+
});
62+
63+
describe('on selected element remove', () => {
64+
beforeEach(async () => {
65+
element.selectedDataSet?.parentElement?.removeChild(
66+
element.selectedDataSet
67+
);
68+
element.doc = cloneTestDoc(doc);
69+
await element.updateComplete;
70+
71+
await element.selectionList.requestUpdate();
72+
});
73+
74+
it('resets selected Element', async () =>
75+
expect(element.selectedDataSet).to.be.undefined);
76+
77+
it('reset selection', async () =>
78+
expect(element.selectionList.selected).to.be.null);
79+
});
80+
81+
describe('on new doc loaded', () => {
82+
beforeEach(async () => {
83+
element.doc = otherDoc;
84+
await element.updateComplete;
85+
86+
await element.selectionList.requestUpdate();
87+
});
88+
89+
it('does not reset selected Element', async () =>
90+
expect(element.selectedDataSet).to.be.undefined);
91+
92+
it('does not reset selection', async () =>
93+
expect(element.selectionList.selected).to.be.null);
94+
});
3695
});
3796
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export function cloneTestDoc(doc: XMLDocument): XMLDocument {
2+
const newDoc = document.implementation.createDocument(
3+
doc.lookupNamespaceURI(''),
4+
doc.documentElement.tagName,
5+
doc.doctype
6+
);
7+
8+
newDoc.documentElement.replaceWith(doc.documentElement);
9+
10+
return newDoc;
11+
}

0 commit comments

Comments
 (0)