diff --git a/public/js/plugins.js b/public/js/plugins.js index 13bd52793b..3bf4cfe0ff 100644 --- a/public/js/plugins.js +++ b/public/js/plugins.js @@ -55,6 +55,13 @@ export const officialPlugins = [ default: true, kind: 'editor', }, + { + name: 'Publisher', + src: '/src/editors/Publisher.js', + icon: 'publish', + default: false, + kind: 'editor', + }, { name: 'Open project', src: '/src/menu/OpenProject.js', diff --git a/src/editors/Publisher.ts b/src/editors/Publisher.ts new file mode 100644 index 0000000000..022ae88b4c --- /dev/null +++ b/src/editors/Publisher.ts @@ -0,0 +1,94 @@ +import { + css, + html, + LitElement, + property, + state, + TemplateResult, +} from 'lit-element'; +import { classMap } from 'lit-html/directives/class-map'; + +import '@material/mwc-formfield'; +import '@material/mwc-radio'; + +import './publisher/report-control-editor.js'; +import './publisher/gse-control-editor.js'; +import './publisher/sampled-value-control-editor.js'; +import './publisher/data-set-editor.js'; + +/** An editor [[`plugin`]] to configure `Report`, `GOOSE`, `SampledValue` control blocks and its `DataSet` */ +export default class PublisherPlugin extends LitElement { + /** The document being edited as provided to plugins by [[`OpenSCD`]]. */ + @property({ attribute: false }) + doc!: XMLDocument; + @state() + private publisherType: 'Report' | 'GOOSE' | 'SampledValue' | 'DataSet' = + 'GOOSE'; + + render(): TemplateResult { + return html`
+ (this.publisherType = 'Report')} + > (this.publisherType = 'GOOSE')} + > (this.publisherType = 'SampledValue')} + > (this.publisherType = 'DataSet')} + > +
+ `; + } + + static styles = css` + .hidden { + display: none; + } + + .publishertypeselector { + margin: 4px 8px 16px; + background-color: var(--mdc-theme-surface); + width: calc(100% - 16px); + justify-content: space-around; + } + `; +} diff --git a/src/editors/publisher/data-set-editor.ts b/src/editors/publisher/data-set-editor.ts new file mode 100644 index 0000000000..599047ec58 --- /dev/null +++ b/src/editors/publisher/data-set-editor.ts @@ -0,0 +1,59 @@ +import { + css, + customElement, + html, + LitElement, + property, + TemplateResult, +} from 'lit-element'; +import { compareNames, identity } from '../../foundation.js'; + +@customElement('data-set-editor') +export class DataSetEditor extends LitElement { + /** The document being edited as provided to plugins by [[`OpenSCD`]]. */ + @property({ attribute: false }) + doc!: XMLDocument; + + renderList(): TemplateResult { + return html`${Array.from(this.doc.querySelectorAll('IED')) + .sort(compareNames) + .flatMap(ied => { + const ieditem = html` + ${ied.getAttribute('name')} + developer_board + +
  • `; + + const dataSets = Array.from(ied.querySelectorAll('DataSet')).map( + reportCb => + html`${reportCb.getAttribute('name')}${identity(reportCb)} + ` + ); + + return [ieditem, ...dataSets]; + })}
    `; + } + + render(): TemplateResult { + return html`${this.renderList()}`; + } + + static styles = css` + filtered-list { + margin: 4px 8px 16px; + background-color: var(--mdc-theme-surface); + } + + .listitem.header { + font-weight: 500; + } + `; +} diff --git a/src/editors/publisher/gse-control-editor.ts b/src/editors/publisher/gse-control-editor.ts new file mode 100644 index 0000000000..3a23798c68 --- /dev/null +++ b/src/editors/publisher/gse-control-editor.ts @@ -0,0 +1,71 @@ +import { + css, + customElement, + html, + LitElement, + property, + TemplateResult, +} from 'lit-element'; + +import '@material/mwc-list/mwc-list-item'; +import '@material/mwc-icon'; + +import '../../filtered-list.js'; +import { compareNames, identity } from '../../foundation.js'; +import { gooseIcon } from '../../icons/icons.js'; + +@customElement('gse-control-editor') +export class GseControlEditor extends LitElement { + /** The document being edited as provided to plugins by [[`OpenSCD`]]. */ + @property({ attribute: false }) + doc!: XMLDocument; + + renderList(): TemplateResult { + return html`${Array.from(this.doc.querySelectorAll('IED')) + .sort(compareNames) + .flatMap(ied => { + const ieditem = html` + ${ied.getAttribute('name')} + developer_board + +
  • `; + + const gseControls = Array.from( + ied.querySelectorAll('GSEControl') + ).map( + reportCb => + html`${reportCb.getAttribute('name')}${identity(reportCb)} + ${gooseIcon} + ` + ); + + return [ieditem, ...gseControls]; + })}
    `; + } + + render(): TemplateResult { + return html`${this.renderList()}`; + } + + static styles = css` + filtered-list { + margin: 4px 8px 16px; + background-color: var(--mdc-theme-surface); + } + + .listitem.header { + font-weight: 500; + } + `; +} diff --git a/src/editors/publisher/report-control-editor.ts b/src/editors/publisher/report-control-editor.ts new file mode 100644 index 0000000000..f40f9bc479 --- /dev/null +++ b/src/editors/publisher/report-control-editor.ts @@ -0,0 +1,69 @@ +import { + css, + customElement, + html, + LitElement, + property, + TemplateResult, +} from 'lit-element'; + +import '@material/mwc-list/mwc-list-item'; +import '@material/mwc-icon'; + +import '../../filtered-list.js'; +import { compareNames, identity } from '../../foundation.js'; +import { reportIcon } from '../../icons/icons.js'; + +@customElement('report-control-editor') +export class ReportControlEditor extends LitElement { + /** The document being edited as provided to plugins by [[`OpenSCD`]]. */ + @property({ attribute: false }) + doc!: XMLDocument; + + renderList(): TemplateResult { + return html`${Array.from(this.doc.querySelectorAll('IED')) + .sort(compareNames) + .flatMap(ied => { + const ieditem = html` + ${ied.getAttribute('name')} + developer_board + +
  • `; + + const reports = Array.from(ied.querySelectorAll('ReportControl')).map( + reportCb => + html`${reportCb.getAttribute('name')}${identity(reportCb)} + ${reportIcon} + ` + ); + + return [ieditem, ...reports]; + })}
    `; + } + + render(): TemplateResult { + return html`${this.renderList()}`; + } + + static styles = css` + filtered-list { + margin: 4px 8px 16px; + background-color: var(--mdc-theme-surface); + } + + .listitem.header { + font-weight: 500; + } + `; +} diff --git a/src/editors/publisher/sampled-value-control-editor.ts b/src/editors/publisher/sampled-value-control-editor.ts new file mode 100644 index 0000000000..e179066f2b --- /dev/null +++ b/src/editors/publisher/sampled-value-control-editor.ts @@ -0,0 +1,71 @@ +import { + css, + customElement, + html, + LitElement, + property, + TemplateResult, +} from 'lit-element'; + +import '@material/mwc-list/mwc-list-item'; +import '@material/mwc-icon'; + +import '../../filtered-list.js'; +import { compareNames, identity } from '../../foundation.js'; +import { smvIcon } from '../../icons/icons.js'; + +@customElement('sampled-value-control-editor') +export class SampledValueControlEditor extends LitElement { + /** The document being edited as provided to plugins by [[`OpenSCD`]]. */ + @property({ attribute: false }) + doc!: XMLDocument; + + renderList(): TemplateResult { + return html`${Array.from(this.doc.querySelectorAll('IED')) + .sort(compareNames) + .flatMap(ied => { + const ieditem = html` + ${ied.getAttribute('name')} + developer_board + +
  • `; + + const sampledValueControls = Array.from( + ied.querySelectorAll('SampledValueControl') + ).map( + reportCb => + html`${reportCb.getAttribute('name')}${identity(reportCb)} + ${smvIcon} + ` + ); + + return [ieditem, ...sampledValueControls]; + })}
    `; + } + + render(): TemplateResult { + return html`${this.renderList()}`; + } + + static styles = css` + filtered-list { + margin: 4px 8px 16px; + background-color: var(--mdc-theme-surface); + } + + .listitem.header { + font-weight: 500; + } + `; +} diff --git a/test/integration/__snapshots__/open-scd.test.snap.js b/test/integration/__snapshots__/open-scd.test.snap.js index dcbc6a55d9..10a651d2a8 100644 --- a/test/integration/__snapshots__/open-scd.test.snap.js +++ b/test/integration/__snapshots__/open-scd.test.snap.js @@ -683,6 +683,21 @@ snapshots["open-scd looks like its snapshot"] = Templates + + + publish + + Publisher + { + customElements.define('publisher-plugin', Publisher); + + let element: Publisher; + + beforeEach(async () => { + element = await fixture(html``); + }); + + it('per default looks like the latest snapshot', async () => + await expect(element).shadowDom.to.equalSnapshot()); + + it('displays report-control-editor with selected Report publisherType', async () => { + element + .shadowRoot!.querySelector('mwc-radio[value="Report"]')! + .click(); + + await element.requestUpdate(); + + await expect(element).shadowDom.to.equalSnapshot(); + }); + + it('displays sampled-value-control-editor with selected SampledValue publisherType', async () => { + element + .shadowRoot!.querySelector( + 'mwc-radio[value="SampledValue"]' + )! + .click(); + + await element.requestUpdate(); + + await expect(element).shadowDom.to.equalSnapshot(); + }); + + it('displays data-set-editor with selected DataSet publisherType', async () => { + element + .shadowRoot!.querySelector('mwc-radio[value="DataSet"]')! + .click(); + + await element.requestUpdate(); + + await expect(element).shadowDom.to.equalSnapshot(); + }); +}); diff --git a/test/unit/editors/__snapshots__/Publisher.test.snap.js b/test/unit/editors/__snapshots__/Publisher.test.snap.js new file mode 100644 index 0000000000..533d9cd263 --- /dev/null +++ b/test/unit/editors/__snapshots__/Publisher.test.snap.js @@ -0,0 +1,135 @@ +/* @web/test-runner snapshot v1 */ +export const snapshots = {}; + +snapshots["Publisher plugin per default looks like the latest snapshot"] = +`
    + + + + + + + + + + + + + + + + +
    + + + + + +`; +/* end snapshot Publisher plugin per default looks like the latest snapshot */ + +snapshots["Publisher plugin displays report-control-editor with selected Report publisherType"] = +`
    + + + + + + + + + + + + + + + + +
    + + + + + +`; +/* end snapshot Publisher plugin displays report-control-editor with selected Report publisherType */ + +snapshots["Publisher plugin displays sampled-value-control-editor with selected SampledValue publisherType"] = +`
    + + + + + + + + + + + + + + + + +
    + + + + + +`; +/* end snapshot Publisher plugin displays sampled-value-control-editor with selected SampledValue publisherType */ + +snapshots["Publisher plugin displays data-set-editor with selected DataSet publisherType"] = +`
    + + + + + + + + + + + + + + + + +
    + + + + + +`; +/* end snapshot Publisher plugin displays data-set-editor with selected DataSet publisherType */ + diff --git a/test/unit/editors/publisher/__snapshots__/data-set-editor.test.snap.js b/test/unit/editors/publisher/__snapshots__/data-set-editor.test.snap.js new file mode 100644 index 0000000000..04483fc37a --- /dev/null +++ b/test/unit/editors/publisher/__snapshots__/data-set-editor.test.snap.js @@ -0,0 +1,136 @@ +/* @web/test-runner snapshot v1 */ +export const snapshots = {}; + +snapshots["Editor for DataSet element looks like the latest snapshot"] = +` + + + IED1 + + + developer_board + + +
  • +
  • + + + GooseDataSet1 + + + IED1>>CircuitBreaker_CB1>GooseDataSet1 + + + + + IED2 + + + developer_board + + +
  • +
  • + + + GooseDataSet1 + + + IED2>>CBSW>GooseDataSet1 + + + + + dataSet + + + IED2>>CBSW> XSWI 1>dataSet + + + + + dataSet + + + IED2>>CBSW> XSWI 2>dataSet + + + + + IED3 + + + developer_board + + +
  • +
  • + + + PhsMeas1 + + + IED3>>MU01>PhsMeas1 + + +
    +`; +/* end snapshot Editor for DataSet element looks like the latest snapshot */ + diff --git a/test/unit/editors/publisher/__snapshots__/gse-control-editor.test.snap.js b/test/unit/editors/publisher/__snapshots__/gse-control-editor.test.snap.js new file mode 100644 index 0000000000..5d7bbc9364 --- /dev/null +++ b/test/unit/editors/publisher/__snapshots__/gse-control-editor.test.snap.js @@ -0,0 +1,117 @@ +/* @web/test-runner snapshot v1 */ +export const snapshots = {}; + +snapshots["Editor for GSEControl element looks like the latest snapshot"] = +` + + + IED1 + + + developer_board + + +
  • +
  • + + + GCB + + + IED1>>CircuitBreaker_CB1>GCB + + + + + + + GCB2 + + + IED1>>CircuitBreaker_CB1>GCB2 + + + + + + + IED2 + + + developer_board + + +
  • +
  • + + + GCB + + + IED2>>CBSW>GCB + + + + + + + IED3 + + + developer_board + + +
  • +
  • +
    +`; +/* end snapshot Editor for GSEControl element looks like the latest snapshot */ + diff --git a/test/unit/editors/publisher/__snapshots__/report-control-editor.test.snap.js b/test/unit/editors/publisher/__snapshots__/report-control-editor.test.snap.js new file mode 100644 index 0000000000..e5d9eef0d6 --- /dev/null +++ b/test/unit/editors/publisher/__snapshots__/report-control-editor.test.snap.js @@ -0,0 +1,100 @@ +/* @web/test-runner snapshot v1 */ +export const snapshots = {}; + +snapshots["Editor for ReportControl element looks like the latest snapshot"] = +` + + + IED1 + + + developer_board + + +
  • +
  • + + + IED2 + + + developer_board + + +
  • +
  • + + + ReportCb + + + IED2>>CBSW> XSWI 1>ReportCb + + + + + + + ReportCb + + + IED2>>CBSW> XSWI 2>ReportCb + + + + + + + IED3 + + + developer_board + + +
  • +
  • +
    +`; +/* end snapshot Editor for ReportControl element looks like the latest snapshot */ + diff --git a/test/unit/editors/publisher/__snapshots__/sampled-value-control-editor.test.snap.js b/test/unit/editors/publisher/__snapshots__/sampled-value-control-editor.test.snap.js new file mode 100644 index 0000000000..d28b686899 --- /dev/null +++ b/test/unit/editors/publisher/__snapshots__/sampled-value-control-editor.test.snap.js @@ -0,0 +1,83 @@ +/* @web/test-runner snapshot v1 */ +export const snapshots = {}; + +snapshots["Editor for SampledValueControl element looks like the latest snapshot"] = +` + + + IED1 + + + developer_board + + +
  • +
  • + + + IED2 + + + developer_board + + +
  • +
  • + + + IED3 + + + developer_board + + +
  • +
  • + + + MSVCB01 + + + IED3>>MU01>MSVCB01 + + + + +
    +`; +/* end snapshot Editor for SampledValueControl element looks like the latest snapshot */ + diff --git a/test/unit/editors/publisher/data-set-editor.test.ts b/test/unit/editors/publisher/data-set-editor.test.ts new file mode 100644 index 0000000000..d7d065ac1e --- /dev/null +++ b/test/unit/editors/publisher/data-set-editor.test.ts @@ -0,0 +1,22 @@ +import { expect, fixture, html } from '@open-wc/testing'; + +import '../../../../src/editors/publisher/data-set-editor.js'; +import { DataSetEditor } from '../../../../src/editors/publisher/data-set-editor.js'; + +describe('Editor for DataSet element', () => { + let doc: XMLDocument; + let element: DataSetEditor; + + beforeEach(async () => { + doc = await fetch('/test/testfiles/valid2007B4.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + + element = await fixture( + html`` + ); + }); + + it('looks like the latest snapshot', async () => + await expect(element).shadowDom.to.equalSnapshot()); +}); diff --git a/test/unit/editors/publisher/gse-control-editor.test.ts b/test/unit/editors/publisher/gse-control-editor.test.ts new file mode 100644 index 0000000000..d7c578f9f7 --- /dev/null +++ b/test/unit/editors/publisher/gse-control-editor.test.ts @@ -0,0 +1,22 @@ +import { expect, fixture, html } from '@open-wc/testing'; + +import '../../../../src/editors/publisher/gse-control-editor.js'; +import { GseControlEditor } from '../../../../src/editors/publisher/gse-control-editor.js'; + +describe('Editor for GSEControl element', () => { + let doc: XMLDocument; + let element: GseControlEditor; + + beforeEach(async () => { + doc = await fetch('/test/testfiles/valid2007B4.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + + element = await fixture( + html`` + ); + }); + + it('looks like the latest snapshot', async () => + await expect(element).shadowDom.to.equalSnapshot()); +}); diff --git a/test/unit/editors/publisher/report-control-editor.test.ts b/test/unit/editors/publisher/report-control-editor.test.ts new file mode 100644 index 0000000000..659f140a87 --- /dev/null +++ b/test/unit/editors/publisher/report-control-editor.test.ts @@ -0,0 +1,22 @@ +import { expect, fixture, html } from '@open-wc/testing'; + +import '../../../../src/editors/publisher/report-control-editor.js'; +import { ReportControlEditor } from '../../../../src/editors/publisher/report-control-editor.js'; + +describe('Editor for ReportControl element', () => { + let doc: XMLDocument; + let element: ReportControlEditor; + + beforeEach(async () => { + doc = await fetch('/test/testfiles/valid2007B4.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + + element = await fixture( + html`` + ); + }); + + it('looks like the latest snapshot', async () => + await expect(element).shadowDom.to.equalSnapshot()); +}); diff --git a/test/unit/editors/publisher/sampled-value-control-editor.test.ts b/test/unit/editors/publisher/sampled-value-control-editor.test.ts new file mode 100644 index 0000000000..d47f0afafe --- /dev/null +++ b/test/unit/editors/publisher/sampled-value-control-editor.test.ts @@ -0,0 +1,24 @@ +import { expect, fixture, html } from '@open-wc/testing'; + +import '../../../../src/editors/publisher/sampled-value-control-editor.js'; +import { SampledValueControlEditor } from '../../../../src/editors/publisher/sampled-value-control-editor.js'; + +describe('Editor for SampledValueControl element', () => { + let doc: XMLDocument; + let element: SampledValueControlEditor; + + beforeEach(async () => { + doc = await fetch('/test/testfiles/valid2007B4.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + + element = await fixture( + html`` + ); + }); + + it('looks like the latest snapshot', async () => + await expect(element).shadowDom.to.equalSnapshot()); +});