From e8f4d5abbf122d33c171135d7bce180bc95e52d6 Mon Sep 17 00:00:00 2001 From: Dennis Labordus Date: Tue, 27 Sep 2022 08:47:03 +0200 Subject: [PATCH 1/3] Add filtering on labesl when opening SCL File and small improvements in Save menu items Signed-off-by: Dennis Labordus --- src/compas/CompasOpen.ts | 6 +- src/compas/CompasSave.ts | 47 +++-- src/compas/CompasSclList.ts | 166 ++++++++++++++---- src/menu/CompasCompareIED.ts | 2 +- src/menu/CompasImportIEDs.ts | 29 +-- src/menu/CompasMerge.ts | 36 ++-- src/menu/CompasOpen.ts | 95 ++++++---- src/menu/CompasSave.ts | 23 +-- src/menu/CompasSaveAs.ts | 29 +-- src/menu/CompasUpdateSubstation.ts | 2 +- src/oscd-filter-button.ts | 8 +- src/translations/de.ts | 5 + src/translations/en.ts | 7 +- .../oscd-filter-button.test.snap.js | 44 +++++ .../compas/CompasSclDataServiceResponses.ts | 35 +++- test/unit/compas/CompasSclList.test.ts | 117 ++++++++++-- .../__snapshots__/CompasSclList.test.snap.js | 96 ++++++++-- test/unit/menu/CompasOpen.test.ts | 19 ++ .../__snapshots__/CompasOpen.test.snap.js | 22 +++ test/unit/oscd-filter-button.test.ts | 29 +++ 20 files changed, 644 insertions(+), 173 deletions(-) create mode 100644 test/unit/menu/CompasOpen.test.ts create mode 100644 test/unit/menu/__snapshots__/CompasOpen.test.snap.js diff --git a/src/compas/CompasOpen.ts b/src/compas/CompasOpen.ts index 5b310fbf6..4473bf4b6 100644 --- a/src/compas/CompasOpen.ts +++ b/src/compas/CompasOpen.ts @@ -21,7 +21,7 @@ import '../WizardDivider.js'; import './CompasSclTypeList.js'; import './CompasSclList.js'; -/* Event that will be used when a SCL Document is retrieved. */ +/* Event that will be used when an SCL Document is retrieved. */ export interface DocRetrievedDetail { localFile: boolean; doc: Document; @@ -33,7 +33,7 @@ export function newDocRetrievedEvent( doc: Document, docName?: string ): DocRetrievedEvent { - return new CustomEvent('docRetrieved', { + return new CustomEvent('doc-retrieved', { bubbles: true, composed: true, detail: { localFile, doc, docName }, @@ -110,7 +110,7 @@ export default class CompasOpenElement extends LitElement { type: this.selectedType ?? '', })}

+ @scl-selected=${(evt: SclSelectedEvent) => this.dispatchEvent( newPendingStateEvent( this.getSclDocument(evt.detail.docId) diff --git a/src/compas/CompasSave.ts b/src/compas/CompasSave.ts index a35b08c09..d223d4977 100644 --- a/src/compas/CompasSave.ts +++ b/src/compas/CompasSave.ts @@ -4,6 +4,7 @@ import { html, LitElement, property, + PropertyValues, query, TemplateResult, } from 'lit-element'; @@ -42,6 +43,15 @@ import './CompasLabelsField.js'; import './CompasLoading.js'; import './CompasSclTypeSelect.js'; +/* Event that will be used when an SCL Document is saved. */ +export type DocSavedEvent = CustomEvent; +export function newDocSavedEvent(): DocSavedEvent { + return new CustomEvent('doc-saved', { + bubbles: true, + composed: true, + }); +} + @customElement('compas-save') export default class CompasSaveElement extends CompasExistsIn(LitElement) { @property() @@ -62,6 +72,17 @@ export default class CompasSaveElement extends CompasExistsIn(LitElement) { @query('compas-labels-field') private labelsField!: CompasLabelsFieldElement; + protected updated(_changedProperties: PropertyValues): void { + super.updated(_changedProperties); + + // When the document is updated, we reset the selected IED. + if (_changedProperties.has('doc')) { + if (this.commentField) { + this.commentField.value = null; + } + } + } + valid(): boolean { if (!this.existInCompas) { return this.nameField.checkValidity() && this.sclTypeRadioGroup.valid(); @@ -79,16 +100,14 @@ export default class CompasSaveElement extends CompasExistsIn(LitElement) { this.labelsField.updateLabelsInPrivateElement(privateElement!); } - private async addSclToCompas(doc: XMLDocument): Promise { + private async addSclToCompas(doc: XMLDocument): Promise { const name = stripExtensionFromName(this.nameField.value); const comment = this.commentField.value; const docType = this.sclTypeRadioGroup.getSelectedValue() ?? ''; - let success = false; await CompasSclDataService() .addSclDocument(docType, { sclName: name, comment: comment, doc: doc }) .then(sclDocument => { - this.commentField.value = null; updateDocumentInOpenSCD(this, sclDocument); this.dispatchEvent( @@ -97,22 +116,20 @@ export default class CompasSaveElement extends CompasExistsIn(LitElement) { title: get('compas.save.addSuccess'), }) ); - success = true; + + this.dispatchEvent(newDocSavedEvent()); }) .catch(reason => createLogEvent(this, reason)); - - return success; } private async updateSclInCompas( docId: string, docName: string, doc: XMLDocument - ): Promise { + ): Promise { const changeSet = this.changeSetRadiogroup.getSelectedValue(); const comment = this.commentField.value; const docType = getTypeFromDocName(docName); - let success = false; await CompasSclDataService() .updateSclDocument(docType, docId, { @@ -121,7 +138,6 @@ export default class CompasSaveElement extends CompasExistsIn(LitElement) { doc: doc, }) .then(sclDocument => { - this.commentField.value = null; updateDocumentInOpenSCD(this, sclDocument); this.dispatchEvent( @@ -130,19 +146,18 @@ export default class CompasSaveElement extends CompasExistsIn(LitElement) { title: get('compas.save.updateSuccess'), }) ); - success = true; + + this.dispatchEvent(newDocSavedEvent()); }) .catch(reason => createLogEvent(this, reason)); - - return success; } - async saveToCompas(): Promise { + async saveToCompas(): Promise { this.updateLabels(); if (!this.docId || !this.existInCompas) { - return this.addSclToCompas(this.doc); + await this.addSclToCompas(this.doc); } else { - return this.updateSclInCompas(this.docId, this.docName, this.doc); + await this.updateSclInCompas(this.docId, this.docName, this.doc); } } @@ -153,6 +168,8 @@ export default class CompasSaveElement extends CompasExistsIn(LitElement) { @click=${() => { this.updateLabels(); saveDocumentToFile(this.doc, this.docName); + + this.dispatchEvent(newDocSavedEvent()); }} > diff --git a/src/compas/CompasSclList.ts b/src/compas/CompasSclList.ts index 76ff97b7f..767ab1cdc 100644 --- a/src/compas/CompasSclList.ts +++ b/src/compas/CompasSclList.ts @@ -1,8 +1,11 @@ import { + css, customElement, html, LitElement, property, + PropertyValues, + state, TemplateResult, } from 'lit-element'; import { translate } from 'lit-translate'; @@ -10,6 +13,11 @@ import { translate } from 'lit-translate'; import '@material/mwc-list'; import '@material/mwc-list/mwc-list-item'; +import { SelectedItemsChangedEvent } from '../oscd-filter-button.js'; + +import '../filtered-list.js'; +import '../oscd-filter-button.js'; + import { CompasSclDataService, SDS_NAMESPACE, @@ -21,7 +29,7 @@ export interface SclSelectedDetail { } export type SclSelectedEvent = CustomEvent; export function newSclSelectedEvent(docId: string): SclSelectedEvent { - return new CustomEvent('sclSelected', { + return new CustomEvent('scl-selected', { bubbles: true, composed: true, detail: { docId }, @@ -30,54 +38,146 @@ export function newSclSelectedEvent(docId: string): SclSelectedEvent { @customElement('compas-scl-list') export class CompasSclList extends LitElement { - @property({ type: String }) - type = ''; - @property() - scls!: Element[]; + type?: string; + + @state() + private items?: Element[]; + + @state() + private labels: string[] = []; + + @state() + private selectedLabels: string[] = []; + + @state() + private get filteredItems(): Element[] | undefined { + // If items are still being retrieved, return undefined. + if (!this.items) { + return undefined; + } + + // If all labels are selected, show all items, including the ones not having a Label. + if (this.labels.length === this.selectedLabels.length) { + return this.items; + } + + return this.items.filter(item => { + const labels = Array.from(item.querySelectorAll('Label') ?? []) + .map(element => element.textContent) + .filter(value => !!value) as string[]; + return ( + labels.filter(label => this.selectedLabels.includes(label)).length > 0 + ); + }); + } firstUpdated(): void { this.fetchData(); } + protected updated(_changedProperties: PropertyValues): void { + super.updated(_changedProperties); + + // When the document is updated, we reset the selected IED. + if (_changedProperties.has('type')) { + this.items = undefined; + this.labels = []; + this.selectedLabels = []; + this.fetchData(); + } + } + fetchData(): void { - CompasSclDataService() - .listScls(this.type) - .then(xmlResponse => { - this.scls = Array.from(xmlResponse.querySelectorAll('Item') ?? []); - }); + if (this.type) { + CompasSclDataService() + .listScls(this.type) + .then(xmlResponse => { + this.items = Array.from(xmlResponse.querySelectorAll('Item') ?? []); + this.labels = Array.from( + new Set( + Array.from(xmlResponse.querySelectorAll('Label') ?? []) + .map(element => element.textContent) + .filter(value => !!value) + ) + ) as string[]; + this.selectedLabels = this.labels; + }); + } } render(): TemplateResult { - if (!this.scls) { + if (!this.items) { return html` `; } - if (this.scls?.length <= 0) { + if (this.items?.length <= 0) { return html` ${translate('compas.noScls')} `; } - return html` - ${this.scls.map(item => { - const id = - item.getElementsByTagNameNS(SDS_NAMESPACE, 'Id').item(0)! - .textContent ?? ''; - let name = - item.getElementsByTagNameNS(SDS_NAMESPACE, 'Name').item(0)! - .textContent ?? ''; - if (name === '') { - name = id; - } - const version = - item.getElementsByTagNameNS(SDS_NAMESPACE, 'Version').item(0)! - .textContent ?? ''; - return html` this.dispatchEvent(newSclSelectedEvent(id))} + const filteredItems = this.filteredItems; + return html`
+ ${translate('compas.sclFilter')} + - ${name} (${version}) - `; - })} - `; + ${this.labels.map(label => { + return html` + ${label} + `; + })} + +
+ ${filteredItems && filteredItems.length > 0 + ? html` + ${filteredItems.map(item => { + const id = + item.getElementsByTagNameNS(SDS_NAMESPACE, 'Id').item(0)! + .textContent ?? ''; + let name = + item.getElementsByTagNameNS(SDS_NAMESPACE, 'Name').item(0)! + .textContent ?? ''; + if (name === '') { + name = id; + } + const version = + item.getElementsByTagNameNS(SDS_NAMESPACE, 'Version').item(0)! + .textContent ?? ''; + return html` this.dispatchEvent(newSclSelectedEvent(id))} + > + ${name} (${version}) + `; + })} + ` + : html` + + ${translate('compas.noFilteredScls')} + + `} `; } + + static styles = css` + .filters { + padding-left: var(--mdc-list-side-padding, 16px); + display: flex; + } + + .filters > span { + line-height: 48px; + } + `; } diff --git a/src/menu/CompasCompareIED.ts b/src/menu/CompasCompareIED.ts index 8c986e58e..8ada7662f 100644 --- a/src/menu/CompasCompareIED.ts +++ b/src/menu/CompasCompareIED.ts @@ -15,7 +15,7 @@ export default class CompasCompareIEDPlugin extends CompareIEDPlugin { */ protected renderSelectTemplateFile(): TemplateResult { return html` { + @doc-retrieved=${(evt: DocRetrievedEvent) => { this.templateDoc = evt.detail.doc; }} > diff --git a/src/menu/CompasImportIEDs.ts b/src/menu/CompasImportIEDs.ts index 86f5c3a68..63f4ddb3b 100644 --- a/src/menu/CompasImportIEDs.ts +++ b/src/menu/CompasImportIEDs.ts @@ -1,12 +1,12 @@ -import {html, LitElement} from 'lit-element'; -import {get} from "lit-translate"; +import { html, LitElement } from 'lit-element'; +import { get } from 'lit-translate'; -import {newWizardEvent, Wizard} from '../foundation.js'; +import { newWizardEvent, Wizard } from '../foundation.js'; -import {DocRetrievedEvent} from "../compas/CompasOpen.js"; -import {prepareImportIEDs} from "./ImportIEDs.js"; +import { DocRetrievedEvent } from '../compas/CompasOpen.js'; +import { prepareImportIEDs } from './ImportIEDs.js'; -import "../compas/CompasOpen.js"; +import '../compas/CompasOpen.js'; export default class CompasImportIEDSMenuPlugin extends LitElement { doc!: XMLDocument; @@ -17,12 +17,13 @@ export default class CompasImportIEDSMenuPlugin extends LitElement { { title: get('compas.importIEDS.title'), content: [ - html` { - await prepareImportIEDs(parent, event.detail.doc, doc); - parent.dispatchEvent(newWizardEvent()); - }}> - - `, + html` { + await prepareImportIEDs(parent, event.detail.doc, doc); + parent.dispatchEvent(newWizardEvent()); + }} + > + `, ], }, ]; @@ -33,6 +34,8 @@ export default class CompasImportIEDSMenuPlugin extends LitElement { } async run(): Promise { - this.dispatchEvent(newWizardEvent(this.importIEDsCompasWizard(this.parent, this.doc))); + this.dispatchEvent( + newWizardEvent(this.importIEDsCompasWizard(this.parent, this.doc)) + ); } } diff --git a/src/menu/CompasMerge.ts b/src/menu/CompasMerge.ts index 2a08af63c..e4297089c 100644 --- a/src/menu/CompasMerge.ts +++ b/src/menu/CompasMerge.ts @@ -1,12 +1,12 @@ -import {html, LitElement} from 'lit-element'; -import {get} from "lit-translate"; +import { html, LitElement } from 'lit-element'; +import { get } from 'lit-translate'; -import {newWizardEvent, Wizard} from '../foundation.js'; -import {mergeWizard} from "../wizards.js"; +import { newWizardEvent, Wizard } from '../foundation.js'; +import { mergeWizard } from '../wizards.js'; -import {DocRetrievedEvent} from "../compas/CompasOpen.js"; +import { DocRetrievedEvent } from '../compas/CompasOpen.js'; -import "../compas/CompasOpen.js"; +import '../compas/CompasOpen.js'; export default class CompasMergeMenuPlugin extends LitElement { doc!: XMLDocument; @@ -17,16 +17,20 @@ export default class CompasMergeMenuPlugin extends LitElement { { title: get('compas.merge.title'), content: [ - html` { - this.parent.dispatchEvent( - newWizardEvent( - mergeWizard(this.doc.documentElement, evt.detail.doc.documentElement) - ) - ); - this.parent.dispatchEvent(newWizardEvent()); - }}> - - `, + html` { + this.parent.dispatchEvent( + newWizardEvent( + mergeWizard( + this.doc.documentElement, + evt.detail.doc.documentElement + ) + ) + ); + this.parent.dispatchEvent(newWizardEvent()); + }} + > + `, ], }, ]; diff --git a/src/menu/CompasOpen.ts b/src/menu/CompasOpen.ts index c050551d1..b84567049 100644 --- a/src/menu/CompasOpen.ts +++ b/src/menu/CompasOpen.ts @@ -1,55 +1,74 @@ -import { html, LitElement } from 'lit-element'; -import { get } from 'lit-translate'; +import { css, html, LitElement, query, TemplateResult } from 'lit-element'; +import { translate } from 'lit-translate'; + +import '@material/mwc-button'; +import '@material/mwc-dialog'; +import { Dialog } from '@material/mwc-dialog'; import { newLogEvent, newOpenDocEvent, newPendingStateEvent, - newWizardEvent, - Wizard, } from '../foundation.js'; -import { DocRetrievedEvent } from '../compas/CompasOpen.js'; +import CompasOpenElement, { DocRetrievedEvent } from '../compas/CompasOpen.js'; import { updateDocumentInOpenSCD } from '../compas/foundation.js'; import '../compas/CompasOpen.js'; export default class CompasOpenMenuPlugin extends LitElement { - private openCompasWizard(): Wizard { - async function openDoc( - plugin: CompasOpenMenuPlugin, - event: DocRetrievedEvent - ): Promise { - if (event.detail.localFile) { - plugin.dispatchEvent(newLogEvent({ kind: 'reset' })); - plugin.dispatchEvent( - newOpenDocEvent(event.detail.doc, event.detail.docName!, { - detail: { docId: undefined }, - }) - ); - plugin.dispatchEvent(newWizardEvent()); - } else { - updateDocumentInOpenSCD(plugin, event.detail.doc, event.detail.docName); - plugin.dispatchEvent(newWizardEvent()); - } - } + @query('mwc-dialog#compas-open-dlg') + dialog!: Dialog; - return [ - { - title: get('compas.open.title'), - content: [ - html` { - this.dispatchEvent(newPendingStateEvent(openDoc(this, event))); - }} - > - `, - ], - }, - ]; - } + @query('compas-open') + compasOpenElement!: CompasOpenElement; async run(): Promise { - this.dispatchEvent(newWizardEvent(this.openCompasWizard())); + this.compasOpenElement.selectedType = undefined; + await this.compasOpenElement.requestUpdate(); + this.dialog.show(); + } + + private async openDoc(event: DocRetrievedEvent): Promise { + if (event.detail.localFile) { + this.dispatchEvent(newLogEvent({ kind: 'reset' })); + this.dispatchEvent( + newOpenDocEvent(event.detail.doc, event.detail.docName!, { + detail: { docId: undefined }, + }) + ); + } else { + updateDocumentInOpenSCD(this, event.detail.doc, event.detail.docName); + } + this.dialog.close(); + } + + render(): TemplateResult { + return html` + { + this.dispatchEvent(newPendingStateEvent(this.openDoc(event))); + }} + > + + + + `; } + + static styles = css` + mwc-dialog { + --mdc-dialog-min-width: 23vw; + --mdc-dialog-max-width: 92vw; + } + `; } diff --git a/src/menu/CompasSave.ts b/src/menu/CompasSave.ts index 4e798d62c..bfa892787 100644 --- a/src/menu/CompasSave.ts +++ b/src/menu/CompasSave.ts @@ -8,12 +8,15 @@ import { } from 'lit-element'; import { translate } from 'lit-translate'; +import '@material/mwc-button'; +import '@material/mwc-dialog'; +import { Dialog } from '@material/mwc-dialog'; + import { newPendingStateEvent } from '../foundation.js'; import CompasSaveElement from '../compas/CompasSave.js'; import '../compas/CompasSave.js'; -import { Dialog } from '@material/mwc-dialog'; export default class CompasSaveMenuPlugin extends LitElement { @property() @@ -23,7 +26,7 @@ export default class CompasSaveMenuPlugin extends LitElement { @property() docId?: string; - @query('mwc-dialog') + @query('mwc-dialog#compas-save-dlg') dialog!: Dialog; @query('compas-save') @@ -31,14 +34,7 @@ export default class CompasSaveMenuPlugin extends LitElement { async run(): Promise { await this.compasSaveElement.requestUpdate(); - this.dialog.open = true; - } - - private async saveToCoMPAS(): Promise { - const success = await this.compasSaveElement.saveToCompas(); - if (success) { - this.dialog.close(); - } + this.dialog.show(); } render(): TemplateResult { @@ -53,6 +49,9 @@ export default class CompasSaveMenuPlugin extends LitElement { .doc="${this.doc}" .docName="${this.docName}" .docId="${this.docId}" + @doc-saved=${() => { + this.dialog.close(); + }} > { if (this.compasSaveElement.valid()) { - this.dispatchEvent(newPendingStateEvent(this.saveToCoMPAS())); + this.dispatchEvent( + newPendingStateEvent(this.compasSaveElement.saveToCompas()) + ); } }} > diff --git a/src/menu/CompasSaveAs.ts b/src/menu/CompasSaveAs.ts index b4407ba26..e065c2f82 100644 --- a/src/menu/CompasSaveAs.ts +++ b/src/menu/CompasSaveAs.ts @@ -8,12 +8,15 @@ import { } from 'lit-element'; import { translate } from 'lit-translate'; +import '@material/mwc-button'; +import '@material/mwc-dialog'; +import { Dialog } from '@material/mwc-dialog'; + import { newPendingStateEvent } from '../foundation.js'; import CompasSaveElement from '../compas/CompasSave.js'; import '../compas/CompasSave.js'; -import { Dialog } from '@material/mwc-dialog'; export default class CompasSaveAsMenuPlugin extends LitElement { @property() @@ -21,21 +24,15 @@ export default class CompasSaveAsMenuPlugin extends LitElement { @property() docName!: string; - @query('mwc-dialog') + @query('mwc-dialog#compas-save-as-dlg') dialog!: Dialog; @query('compas-save') compasSaveElement!: CompasSaveElement; async run(): Promise { - this.dialog.open = true; - } - - private async saveToCoMPAS(): Promise { - const success = await this.compasSaveElement.saveToCompas(); - if (success) { - this.dialog.close(); - } + await this.compasSaveElement.requestUpdate(); + this.dialog.show(); } render(): TemplateResult { @@ -46,7 +43,13 @@ export default class CompasSaveAsMenuPlugin extends LitElement { ${!this.doc || !this.docName ? html`` : html` - + { + this.dialog.close(); + }} + > { if (this.compasSaveElement.valid()) { - this.dispatchEvent(newPendingStateEvent(this.saveToCoMPAS())); + this.dispatchEvent( + newPendingStateEvent(this.compasSaveElement.saveToCompas()) + ); } }} > diff --git a/src/menu/CompasUpdateSubstation.ts b/src/menu/CompasUpdateSubstation.ts index 2706fba07..f0ac105b7 100644 --- a/src/menu/CompasUpdateSubstation.ts +++ b/src/menu/CompasUpdateSubstation.ts @@ -18,7 +18,7 @@ export default class CompasUpdateSubstationMenuPlugin extends LitElement { title: get('compas.updateSubstation.title'), content: [ html` { + @doc-retrieved=${(evt: DocRetrievedEvent) => { mergeSubstation(this, this.doc, evt.detail.doc); this.dispatchEvent(newWizardEvent()); }} diff --git a/src/oscd-filter-button.ts b/src/oscd-filter-button.ts index 424938e06..c3135f95f 100644 --- a/src/oscd-filter-button.ts +++ b/src/oscd-filter-button.ts @@ -26,6 +26,8 @@ export class FilterButton extends FilteredList { header!: TemplateResult | string; @property() icon!: string; + @property({ type: Boolean }) + disabled = false; @query('#filterDialog') private filterDialog!: Dialog; @@ -48,7 +50,11 @@ export class FilterButton extends FilteredList { render(): TemplateResult { return html` - + + + + + +
+ + + + + + + + +
+
    + + +
+ + [close] + +
+`; +/* end snapshot oscd-filter-button is disabled looks like its latest snapshot */ + diff --git a/test/unit/compas/CompasSclDataServiceResponses.ts b/test/unit/compas/CompasSclDataServiceResponses.ts index a2d7b3f2b..a70996c1d 100644 --- a/test/unit/compas/CompasSclDataServiceResponses.ts +++ b/test/unit/compas/CompasSclDataServiceResponses.ts @@ -1,5 +1,5 @@ -import {SDS_NAMESPACE} from "../../../src/compas-services/CompasSclDataService.js"; -import sinon, {SinonStub} from "sinon"; +import { SDS_NAMESPACE } from '../../../src/compas-services/CompasSclDataService.js'; +import sinon, { SinonStub } from 'sinon'; export const TYPE_ENTRY_ELEMENT_NAME = 'Type'; export const BASIC_TYPE_LIST_RESPONSE = ` @@ -28,6 +28,23 @@ export const BASIC_ITEM_LIST_RESPONSE = ` 1.3.0 `; +export const ITEM_LIST_WITH_LABELS_RESPONSE = ` + + + 9883eabb-2e3c-471c-9036-95045d01e3fc + Station-Utrecht-0001 + 2.1.0 + + + + + 771d8940-9024-4c8b-a103-9566f1ba845e + Station-Amsterdam-0001 + 1.3.0 + + + + `; export const VERSION_ENTRY_ELEMENT_NAME = 'HistoryItem'; export const BASIC_VERSIONS_LIST_RESPONSE = ` @@ -57,15 +74,17 @@ export const BASIC_VERSIONS_LIST_RESPONSE = ` `; -export function stubFetchResponseFunction(element: any, - functionName: string, - response: string | undefined, - listElementName: string, - callback: (result: Element[]) => any): SinonStub { +export function stubFetchResponseFunction( + element: any, + functionName: string, + response: string | undefined, + listElementName: string, + callback: (result: Element[]) => any +): SinonStub { return sinon.stub(element, functionName).callsFake(() => { if (response) { const parser = new DOMParser(); - const document = parser.parseFromString(response, "text/xml"); + const document = parser.parseFromString(response, 'text/xml'); callback(Array.from(document.querySelectorAll(listElementName) ?? [])); } else { callback([]); diff --git a/test/unit/compas/CompasSclList.test.ts b/test/unit/compas/CompasSclList.test.ts index 97a7fb8c6..b7ee5c851 100644 --- a/test/unit/compas/CompasSclList.test.ts +++ b/test/unit/compas/CompasSclList.test.ts @@ -5,6 +5,7 @@ import { ListItem } from '@material/mwc-list/mwc-list-item'; import { BASIC_ITEM_LIST_RESPONSE, ITEM_ENTRY_ELEMENT_NAME, + ITEM_LIST_WITH_LABELS_RESPONSE, stubFetchResponseFunction, } from './CompasSclDataServiceResponses.js'; import { CompasSclList } from '../../../src/compas/CompasSclList.js'; @@ -17,7 +18,7 @@ describe('compas-scl-list', () => { let element: CompasSclList; let stub: SinonStub; - describe('show-loading', () => { + describe('when list still needs to be loaded', () => { beforeEach(async () => { element = fixtureSync( html`` @@ -45,7 +46,7 @@ describe('compas-scl-list', () => { }); }); - describe('no-items-in-list', () => { + describe('when there are no items found in CoMPAS', () => { beforeEach(async () => { element = fixtureSync( html`` @@ -57,12 +58,12 @@ describe('compas-scl-list', () => { undefined, ITEM_ENTRY_ELEMENT_NAME, (result: Element[]) => { - element.scls = result; + element['items'] = result; } ); await element; - await waitUntil(() => element.scls !== undefined); + await waitUntil(() => element['items'] !== undefined); }); afterEach(() => { @@ -75,7 +76,7 @@ describe('compas-scl-list', () => { }); }); - describe('after-list-loaded', () => { + describe('when there items without labels', () => { beforeEach(async () => { element = fixtureSync( html`` @@ -87,12 +88,12 @@ describe('compas-scl-list', () => { BASIC_ITEM_LIST_RESPONSE, ITEM_ENTRY_ELEMENT_NAME, (result: Element[]) => { - element.scls = result; + element['items'] = result; } ); await element; - await waitUntil(() => element.scls !== undefined); + await waitUntil(() => element['items'] !== undefined); }); afterEach(() => { @@ -101,16 +102,22 @@ describe('compas-scl-list', () => { it('has 2 item entries', () => { expect( - element.shadowRoot!.querySelectorAll('mwc-list > mwc-list-item') + element.shadowRoot!.querySelectorAll('filtered-list > mwc-list-item') ).to.have.length(2); }); + it('filter button for labels is disabled', () => { + expect( + element.shadowRoot!.querySelector('oscd-filter-button#labelsFilter') + ).to.have.attribute('disabled'); + }); + it('selecting the first row will cause open scl method to be called', async () => { const eventSpy = sinon.spy(); - element.addEventListener('sclSelected', eventSpy); + element.addEventListener('scl-selected', eventSpy); (( - element.shadowRoot!.querySelectorAll('mwc-list > mwc-list-item')[0] + element.shadowRoot!.querySelectorAll('filtered-list > mwc-list-item')[0] )).click(); await element.updateComplete; @@ -122,4 +129,94 @@ describe('compas-scl-list', () => { sinon.assert.calledOnce(stub); }); }); + + describe('when there items with labels', () => { + beforeEach(async () => { + element = fixtureSync( + html`` + ); + + stub = stubFetchResponseFunction( + element, + FETCH_FUNCTION, + ITEM_LIST_WITH_LABELS_RESPONSE, + ITEM_ENTRY_ELEMENT_NAME, + (result: Element[]) => { + element['items'] = result; + + const labels = Array.from( + new Set( + result + .map(item => Array.from(item.querySelectorAll('Label'))) + .flatMap(label => label) + .filter(label => !!label) + .map(label => label!.textContent) + .filter(labelValue => !!labelValue) as string[] + ) + ); + if (labels) { + element['labels'] = labels; + element['selectedLabels'] = labels; + } + } + ); + + await element; + await waitUntil(() => element['items'] !== undefined); + }); + + afterEach(() => { + sinon.restore(); + }); + + it('has 3 labels found', () => { + expect(element['labels']).to.have.length(3); + expect(element['selectedLabels']).to.have.length(3); + }); + + it('filter button for labels is enabled', () => { + expect( + element.shadowRoot!.querySelector('oscd-filter-button#labelsFilter') + ).to.have.not.attribute('disabled'); + }); + + it('has 2 item entries', () => { + expect( + element.shadowRoot!.querySelectorAll('filtered-list > mwc-list-item') + ).to.have.length(2); + }); + + it('when filtering on labels only 1 item is shown', async () => { + const filterButton = ( + element.shadowRoot!.querySelector('oscd-filter-button#labelsFilter') + ); + (( + filterButton.shadowRoot!.querySelector('mwc-icon-button') + )).click(); + await element.updateComplete; + + Array.from( + element.shadowRoot!.querySelectorAll( + 'oscd-filter-button#labelsFilter > mwc-check-list-item' + ) + ) + .filter(element => element.getAttribute('value') !== 'Amsterdam') + .forEach(element => (element).click()); + (( + filterButton.shadowRoot!.querySelector( + 'mwc-button[slot="primaryAction"]' + ) + )).click(); + await element.updateComplete; + + expect( + element.shadowRoot!.querySelectorAll('filtered-list > mwc-list-item') + ).to.have.length(1); + }); + + it('looks like the latest snapshot', async () => { + await expect(element).shadowDom.to.equalSnapshot(); + sinon.assert.calledOnce(stub); + }); + }); }); diff --git a/test/unit/compas/__snapshots__/CompasSclList.test.snap.js b/test/unit/compas/__snapshots__/CompasSclList.test.snap.js index f3959e5bc..3547b4908 100644 --- a/test/unit/compas/__snapshots__/CompasSclList.test.snap.js +++ b/test/unit/compas/__snapshots__/CompasSclList.test.snap.js @@ -1,14 +1,16 @@ /* @web/test-runner snapshot v1 */ export const snapshots = {}; -snapshots["compas-scl-list show-loading looks like the latest snapshot"] = -` +snapshots[ + 'compas-scl-list when list still needs to be loaded looks like the latest snapshot' +] = ` `; -/* end snapshot compas-scl-list show-loading looks like the latest snapshot */ +/* end snapshot compas-scl-list when list still needs to be loaded looks like the latest snapshot */ -snapshots["compas-scl-list no-items-in-list looks like the latest snapshot"] = -` +snapshots[ + 'compas-scl-list when there are no items found in CoMPAS looks like the latest snapshot' +] = ` `; -/* end snapshot compas-scl-list no-items-in-list looks like the latest snapshot */ +/* end snapshot compas-scl-list when there are no items found in CoMPAS looks like the latest snapshot */ -snapshots["compas-scl-list after-list-loaded looks like the latest snapshot"] = -` +snapshots[ + 'compas-scl-list when there items without labels looks like the latest snapshot' +] = `
+ + [compas.sclFilter] + + + +
+ Station-Utrecht-0002 (1.3.0) -
+ `; -/* end snapshot compas-scl-list after-list-loaded looks like the latest snapshot */ +/* end snapshot compas-scl-list when there items without labels looks like the latest snapshot */ +snapshots[ + 'compas-scl-list when there items with labels looks like the latest snapshot' +] = `
+ + [compas.sclFilter] + + + + Netherlands + + + Utrecht + + + Amsterdam + + +
+ + + Station-Utrecht-0001 (2.1.0) + + + Station-Amsterdam-0001 (1.3.0) + + +`; +/* end snapshot compas-scl-list when there items with labels looks like the latest snapshot */ diff --git a/test/unit/menu/CompasOpen.test.ts b/test/unit/menu/CompasOpen.test.ts new file mode 100644 index 000000000..0bc6e4024 --- /dev/null +++ b/test/unit/menu/CompasOpen.test.ts @@ -0,0 +1,19 @@ +import { expect, fixture, html } from '@open-wc/testing'; + +import CompasOpenMenuPlugin from '../../../src/menu/CompasOpen.js'; + +describe('compas-open-menu', () => { + if (customElements.get('compare-open-menu') === undefined) + customElements.define('compare-open-menu', CompasOpenMenuPlugin); + + let plugin: CompasOpenMenuPlugin; + + beforeEach(async () => { + plugin = await fixture(html``); + await plugin.updateComplete; + }); + + it('looks like the latest snapshot', async () => { + await expect(plugin).shadowDom.to.equalSnapshot(); + }); +}); diff --git a/test/unit/menu/__snapshots__/CompasOpen.test.snap.js b/test/unit/menu/__snapshots__/CompasOpen.test.snap.js new file mode 100644 index 000000000..88597b1d1 --- /dev/null +++ b/test/unit/menu/__snapshots__/CompasOpen.test.snap.js @@ -0,0 +1,22 @@ +/* @web/test-runner snapshot v1 */ +export const snapshots = {}; + +snapshots["compas-open-menu looks like the latest snapshot"] = +` + + + + + +`; +/* end snapshot compas-open-menu looks like the latest snapshot */ + diff --git a/test/unit/oscd-filter-button.test.ts b/test/unit/oscd-filter-button.test.ts index 7379b16be..32566f6b5 100644 --- a/test/unit/oscd-filter-button.test.ts +++ b/test/unit/oscd-filter-button.test.ts @@ -141,4 +141,33 @@ describe('oscd-filter-button', () => { await expect(element).shadowDom.to.equalSnapshot(); }); }); + + describe('is disabled', () => { + beforeEach(async () => { + element = await fixture( + html` + ${Array.from(listItems).map( + item => + html` + ${item.prim} + ` + )} + ` + ); + await element.requestUpdate(); + await element.updateComplete; + }); + + it('looks like its latest snapshot', async () => { + await expect(element).shadowDom.to.equalSnapshot(); + }); + }); }); From a1f2f6a6792bfc06e13e1cd67934b80ffe797b41 Mon Sep 17 00:00:00 2001 From: Dennis Labordus Date: Tue, 27 Sep 2022 08:58:41 +0200 Subject: [PATCH 2/3] Sorted list of labels. Signed-off-by: Dennis Labordus --- src/compas/CompasSclList.ts | 3 ++- test/unit/compas/CompasSclList.test.ts | 5 ++++- .../compas/__snapshots__/CompasSclList.test.snap.js | 12 ++++++------ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/compas/CompasSclList.ts b/src/compas/CompasSclList.ts index 767ab1cdc..90244b201 100644 --- a/src/compas/CompasSclList.ts +++ b/src/compas/CompasSclList.ts @@ -98,7 +98,8 @@ export class CompasSclList extends LitElement { new Set( Array.from(xmlResponse.querySelectorAll('Label') ?? []) .map(element => element.textContent) - .filter(value => !!value) + .filter(label => !!label) + .sort((label1, label2) => label1!.localeCompare(label2!)) ) ) as string[]; this.selectedLabels = this.labels; diff --git a/test/unit/compas/CompasSclList.test.ts b/test/unit/compas/CompasSclList.test.ts index b7ee5c851..0d00ea9b2 100644 --- a/test/unit/compas/CompasSclList.test.ts +++ b/test/unit/compas/CompasSclList.test.ts @@ -151,7 +151,10 @@ describe('compas-scl-list', () => { .flatMap(label => label) .filter(label => !!label) .map(label => label!.textContent) - .filter(labelValue => !!labelValue) as string[] + .filter(labelValue => !!labelValue) + .sort((label1, label2) => + label1!.localeCompare(label2!) + ) as string[] ) ); if (labels) { diff --git a/test/unit/compas/__snapshots__/CompasSclList.test.snap.js b/test/unit/compas/__snapshots__/CompasSclList.test.snap.js index 3547b4908..a6f73442e 100644 --- a/test/unit/compas/__snapshots__/CompasSclList.test.snap.js +++ b/test/unit/compas/__snapshots__/CompasSclList.test.snap.js @@ -74,9 +74,9 @@ snapshots[ mwc-list-item="" selected="" tabindex="0" - value="Netherlands" + value="Amsterdam" > - Netherlands + Amsterdam - Utrecht + Netherlands - Amsterdam + Utrecht From ebe32fc506a47517f0bf139314b01f9aafc25e1b Mon Sep 17 00:00:00 2001 From: Dennis Labordus Date: Tue, 27 Sep 2022 12:11:17 +0200 Subject: [PATCH 3/3] Used 2 different icons to show filterin is on/off. Signed-off-by: Dennis Labordus --- src/compas/CompasSclList.ts | 20 ++- test/unit/compas/CompasSclList.test.ts | 29 +++- .../__snapshots__/CompasSclList.test.snap.js | 150 ++++++++++++++++-- 3 files changed, 176 insertions(+), 23 deletions(-) diff --git a/src/compas/CompasSclList.ts b/src/compas/CompasSclList.ts index 90244b201..e5bae67a4 100644 --- a/src/compas/CompasSclList.ts +++ b/src/compas/CompasSclList.ts @@ -10,6 +10,7 @@ import { } from 'lit-element'; import { translate } from 'lit-translate'; +import '@material/mwc-icon'; import '@material/mwc-list'; import '@material/mwc-list/mwc-list-item'; @@ -117,11 +118,11 @@ export class CompasSclList extends LitElement {
`; } const filteredItems = this.filteredItems; - return html`
+ return html` +
${translate('compas.sclFilter')} + + + ${this.labels.length != this.selectedLabels.length + ? 'label' + : 'label_off'} + + ${this.labels.map(label => { - return html` @@ -156,7 +165,7 @@ export class CompasSclList extends LitElement { const version = item.getElementsByTagNameNS(SDS_NAMESPACE, 'Version').item(0)! .textContent ?? ''; - return html` this.dispatchEvent(newSclSelectedEvent(id))} > @@ -168,7 +177,8 @@ export class CompasSclList extends LitElement { ${translate('compas.noFilteredScls')} - `} `; + `} + `; } static styles = css` diff --git a/test/unit/compas/CompasSclList.test.ts b/test/unit/compas/CompasSclList.test.ts index 0d00ea9b2..28391eb1c 100644 --- a/test/unit/compas/CompasSclList.test.ts +++ b/test/unit/compas/CompasSclList.test.ts @@ -76,7 +76,7 @@ describe('compas-scl-list', () => { }); }); - describe('when there items without labels', () => { + describe('when there are items without labels', () => { beforeEach(async () => { element = fixtureSync( html`` @@ -130,7 +130,7 @@ describe('compas-scl-list', () => { }); }); - describe('when there items with labels', () => { + describe('when there are items with labels', () => { beforeEach(async () => { element = fixtureSync( html`` @@ -215,6 +215,31 @@ describe('compas-scl-list', () => { expect( element.shadowRoot!.querySelectorAll('filtered-list > mwc-list-item') ).to.have.length(1); + await expect(element).shadowDom.to.equalSnapshot(); + }); + + it('when filtering on labels and all items are hidden', async () => { + const filterButton = ( + element.shadowRoot!.querySelector('oscd-filter-button#labelsFilter') + ); + (( + filterButton.shadowRoot!.querySelector('mwc-icon-button') + )).click(); + await element.updateComplete; + + Array.from( + element.shadowRoot!.querySelectorAll( + 'oscd-filter-button#labelsFilter > mwc-check-list-item' + ) + ).forEach(element => (element).click()); + (( + filterButton.shadowRoot!.querySelector( + 'mwc-button[slot="primaryAction"]' + ) + )).click(); + await element.updateComplete; + + await expect(element).shadowDom.to.equalSnapshot(); }); it('looks like the latest snapshot', async () => { diff --git a/test/unit/compas/__snapshots__/CompasSclList.test.snap.js b/test/unit/compas/__snapshots__/CompasSclList.test.snap.js index a6f73442e..abc920b79 100644 --- a/test/unit/compas/__snapshots__/CompasSclList.test.snap.js +++ b/test/unit/compas/__snapshots__/CompasSclList.test.snap.js @@ -1,16 +1,14 @@ /* @web/test-runner snapshot v1 */ export const snapshots = {}; -snapshots[ - 'compas-scl-list when list still needs to be loaded looks like the latest snapshot' -] = ` +snapshots["compas-scl-list when list still needs to be loaded looks like the latest snapshot"] = +` `; /* end snapshot compas-scl-list when list still needs to be loaded looks like the latest snapshot */ -snapshots[ - 'compas-scl-list when there are no items found in CoMPAS looks like the latest snapshot' -] = ` +snapshots["compas-scl-list when there are no items found in CoMPAS looks like the latest snapshot"] = +` +snapshots["compas-scl-list when there are items without labels looks like the latest snapshot"] = +`
[compas.sclFilter] + + + label_off + +
@@ -55,19 +56,135 @@ snapshots[
`; -/* end snapshot compas-scl-list when there items without labels looks like the latest snapshot */ +/* end snapshot compas-scl-list when there are items without labels looks like the latest snapshot */ -snapshots[ - 'compas-scl-list when there items with labels looks like the latest snapshot' -] = `
+snapshots["compas-scl-list when there are items with labels when filtering on labels only 1 item is shown"] = +`
[compas.sclFilter] + + + label + + + + Amsterdam + + + Netherlands + + + Utrecht + + +
+ + + Station-Amsterdam-0001 (1.3.0) + + +`; +/* end snapshot compas-scl-list when there are items with labels when filtering on labels only 1 item is shown */ + +snapshots["compas-scl-list when there are items with labels when filtering on labels and all items are hidden"] = +`
+ + [compas.sclFilter] + + + + + label + + + + Amsterdam + + + Netherlands + + + Utrecht + + +
+ + + + [compas.noFilteredScls] + + + +`; +/* end snapshot compas-scl-list when there are items with labels when filtering on labels and all items are hidden */ + +snapshots["compas-scl-list when there are items with labels looks like the latest snapshot"] = +`
+ + [compas.sclFilter] + + + + + label_off + + `; -/* end snapshot compas-scl-list when there items with labels looks like the latest snapshot */ +/* end snapshot compas-scl-list when there are items with labels looks like the latest snapshot */ +