From aa2a3414b25c794de2271d9e84159e1c5eacc714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabrice=20PENHO=C3=8BT?= Date: Fri, 17 Sep 2021 18:02:45 +0200 Subject: [PATCH] =?UTF-8?q?S=C3=A9paration=20du=20code=20g=C3=A9n=C3=A9ran?= =?UTF-8?q?t=20les=20filtres,=20avec=20classe=20et=20script=20de=20test=20?= =?UTF-8?q?d=C3=A9di=C3=A9s=20+=20injection=20dans=20la=20classe=20princip?= =?UTF-8?q?ale.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/errors.js | 5 +- src/freeDatas2HTML.ts | 324 ++++++++++++-------------------- src/freeDatas2HTMLInterfaces.ts | 23 ++- src/freeDatas2HTMLSelector.ts | 161 ++++++++++++++++ tests/fixtures.js | 11 +- tests/freeDatas2HTMLSpec.ts | 204 ++++---------------- tests/selectorsSpec.ts | 293 +++++++++++++++++++++++++++++ 7 files changed, 636 insertions(+), 385 deletions(-) create mode 100644 src/freeDatas2HTMLSelector.ts create mode 100644 tests/selectorsSpec.ts diff --git a/src/errors.js b/src/errors.js index 9fe274e..a7bb1be 100644 --- a/src/errors.js +++ b/src/errors.js @@ -9,6 +9,9 @@ module.exports = needPositiveInteger: "Merci de fournir un nombre entier supérieur à zéro pour désigner chaque option de pagination.", needUrl: "Merci de fournir une url valide pour le fichier CSV à parser.", parserFail: "La lecture des données du fichier a échoué.", - selectorFieldNotFound: "Au moins une des colonnes devant servir à filtrer les données n'existe pas dans le fichier.", + selector2HTMLFail: "Le création d'un filtre dans le DOM nécessite l'initialisation de l'élément HTML et du numéro du champs à filter.", sortingColumnsFieldNotFound: "Au moins une des colonnes devant servir à classer les données n'existe pas dans le fichier.", + selectorFieldNotFound: "Au moins une des colonnes devant servir à filtrer les données n'existe pas dans le fichier.", + selectorNeedDatas: "Le création d'un filtre nécessite la transmission des données à filtrer.", + selectorSelectedIndexNotFound: "La valeur sélectionnée n'a pas été trouvée dans la liste des champs.", }; \ No newline at end of file diff --git a/src/freeDatas2HTML.ts b/src/freeDatas2HTML.ts index b11cd27..bc59c01 100644 --- a/src/freeDatas2HTML.ts +++ b/src/freeDatas2HTML.ts @@ -2,38 +2,56 @@ const Papa = require("papaparse"); const errors = require("./errors.js"); const { compare }= require('natural-orderby'); -import { papaParseDatas, papaParseErrors, papaParseMeta } from "./papaParseInterfaces"; -import { domElement, pagination, selectors, sortingColumns, sortingFunctions } from "./freeDatas2HTMLInterfaces"; +import { DOMElement, Pagination, Selectors, SortingColumns, SortingFunctions } from "./freeDatas2HTMLInterfaces"; +import { Selector } from "./freeDatas2HTMLSelector"; -export class freeDatas2HTML +import { PapaParseDatas, PapaParseErrors, PapaParseMeta } from "./papaParseInterfaces"; + +export { Selector } from "./freeDatas2HTMLSelector"; // pour pouvoir l'appeler du même fichier +export class FreeDatas2HTML { - private _datasViewElt: domElement = { id:"", eltDOM:undefined }; + // L'élément HTML où doivent être affichées les données : + private _datasViewElt: DOMElement = { id:"", eltDOM:undefined }; + // Le code HTML résultant (utile ?) : public datasHTML: string = ""; - // Revoir car tous les attributs suivants sont liés aux colonnes/fields des données (créer une classe ?) - private _datasSelectors: selectors[] = []; - private _datasSortingColumns: sortingColumns[] = []; - private _datasSortedColumn: sortingColumns|undefined; - private _datasSortingFunctions: sortingFunctions[] = []; - // Parseur fichier : - private _datasSourceUrl: string = ""; - public parseMeta: papaParseMeta|undefined = undefined; - public parseDatas: papaParseDatas[] = []; - public parseErrors: papaParseErrors[] = []; - public stopIfParseErrors: boolean = false; - // Pagination : - private _pagination: pagination|undefined; + + // L'url où accéder aux données : + private _datasSourceUrl: string = ""; + // Le nom des champs (interfaces à renommer, car PapaParse = cas particulier) : + public parseMetas: PapaParseMeta|undefined = undefined; + // Les données à proprement parler : + public parseDatas: PapaParseDatas[] = []; + // Les erreurs rencontrées durant le parsage : + public parseErrors: PapaParseErrors[] = []; + // Doit-on tout arrêter si une erreur est rencontrée durant la parsage ? + public stopIfParseErrors: boolean = false; + + // Les filtres possible sur certains champs + datasSelectors: Selectors[] = []; + + // Les champs pouvant être classés : + private _datasSortingColumns: SortingColumns[] = []; + // La dernier champ pour lequel le classement a été demandé (prévoir une valeur par défaut ?) + private _datasSortedColumn: SortingColumns|undefined; + // Les fonctions spécifiques de classement pour certains champs : + private _datasSortingFunctions: SortingFunctions[] = []; + // La Pagination : + private _Pagination: Pagination|undefined; + + // Fonction utile pour tester les numéros de colonne : public static isPositiveInteger(nb: number) { return (Number.isInteger(nb) === false || nb <= 0) ? false : true; } - + // Fonction utile pour tester les valeurs de Pagination : public static isNaturalNumber(nb: number) { return (Number.isInteger(nb) === false || nb < 0) ? false : true; } - set datasViewElt(elt: domElement) + // Vérifie que l'élément devant afficher les données existe dans le DOM : + set datasViewElt(elt: DOMElement) { let checkContainerExist=document.getElementById(elt.id); if(checkContainerExist === null) @@ -45,6 +63,7 @@ export class freeDatas2HTML } } + // Vérifie que l'url où chercher les données n'est pas vide : inutile si données dans page ou tranmises set datasSourceUrl(url: string) { if(url.trim().length === 0) @@ -52,68 +71,45 @@ export class freeDatas2HTML else this._datasSourceUrl=url.trim(); } - - set datasSelectors(selectionElts: selectors[]) - { - this._datasSelectors=[]; - let checkContainerExist: HTMLElement|null; - for(let i in selectionElts) - { - checkContainerExist=document.getElementById(selectionElts[i].id); - if(checkContainerExist === null) - console.error(errors.elementNotFound+selectionElts[i].id); - else if(freeDatas2HTML.isNaturalNumber(selectionElts[i].datasFieldNb) === false) - console.error(errors.needNaturalNumber); - else - { - selectionElts[i].eltDOM=checkContainerExist; - if(selectionElts[i].separator !== undefined && selectionElts[i].separator === "") - selectionElts[i].separator=undefined; - this._datasSelectors.push(selectionElts[i]); - } - } - } - - get datasSelectors() : selectors[] - { - return this._datasSelectors; - } - - set datasSortingColumns(sortingColumns: sortingColumns[]) + + // Vérifie que les numéros de champs devant permettre le classement sont cohérents + // Initialise le sens de classement à undefined + set datasSortingColumns(SortingColumns: SortingColumns[]) { this._datasSortingColumns=[]; - for(let i = 0; i < sortingColumns.length; i++) + for(let i = 0; i < SortingColumns.length; i++) { - if(freeDatas2HTML.isNaturalNumber(sortingColumns[i].datasFieldNb) === false) + if(FreeDatas2HTML.isNaturalNumber(SortingColumns[i].datasFieldNb) === false) console.error(errors.needNaturalNumber); else { - sortingColumns[i].order=undefined; - this._datasSortingColumns.push(sortingColumns[i]); + SortingColumns[i].order=undefined; + this._datasSortingColumns.push(SortingColumns[i]); } } } - get datasSortingColumns() : sortingColumns[] + // Retourne la liste des champs pouvant être classés + get datasSortingColumns() : SortingColumns[] { return this._datasSortingColumns; } - // Attention : une fonction de classement peut aussi bien servir à une colonne triable, qu'à une colonne servant à filtrer les données - set datasSortingFunctions(sortingFunctions: sortingFunctions[]) + // Vérifie que les numéros de champs pour lesquels il y a des fonctions de classement spécifiques sont cohérents + set datasSortingFunctions(SortingFunctions: SortingFunctions[]) { this._datasSortingFunctions=[]; - for(let i = 0; i < sortingFunctions.length; i++) + for(let i = 0; i < SortingFunctions.length; i++) { - if(freeDatas2HTML.isNaturalNumber(sortingFunctions[i].datasFieldNb) === false) + if(FreeDatas2HTML.isNaturalNumber(SortingFunctions[i].datasFieldNb) === false) console.error(errors.needNaturalNumber); else - this._datasSortingFunctions.push(sortingFunctions[i]); + this._datasSortingFunctions.push(SortingFunctions[i]); } } - // Retourne la fonction spécifique de classement associée à une colonne - public getSortingFunctionForField(datasFieldNb: number): sortingFunctions|undefined + // Retourne la fonction spécifique de classement associée à un champ + public getSortingFunctionForField(datasFieldNb: number): SortingFunctions|undefined { for(let i in this._datasSortingFunctions) { @@ -123,10 +119,10 @@ export class freeDatas2HTML return undefined; } - // Long et tortueux ! créer une classe dédiée ? - set pagination(config: pagination) + // Vérifie la cohérence de toutes les options de pagination reçues : + set Pagination(config: Pagination) { - this._pagination={}; + this._Pagination={}; // Si une valeur par défaut est fournie ou des valeurs en option, un id valide doit être aussi fourni pour recueillir le sélecteur de pages : if(config.selectedValue !== undefined || config.options !== undefined) { @@ -137,55 +133,60 @@ export class freeDatas2HTML throw new Error(errors.elementNotFound+config.pages.displayElement.id); else { - this.pagination.pages= + this.Pagination.pages = { displayElement: { id:config.pages.displayElement.id, eltDOM: checkContainerExist }, - name: (config.pages.name) ? config.pages.name : "Pages :", - selectedValue:1,// 1ère page affichée par défaut + name: (config.pages.name) ? config.pages.name : "Pages :", // rendre obligatoire cette option s'il doit y avoir affichage ? + selectedValue:1, // c'est la 1ère page qui est affichée par défaut } } } + // Les options de Pagination proposées à l'utilisateur : if(config.options !== undefined) { + // Un élément HTML doit exister pour accueillir les options : let checkContainerExist=document.getElementById(config.options.displayElement.id); if(checkContainerExist === null) throw new Error(errors.elementNotFound+config.options.displayElement.id); else { + // Seules des entiers positifs sont possibles for(let i = 0; i < config.options.values.length; i++) { - if(freeDatas2HTML.isPositiveInteger(config.options.values[i]) === false) + if(FreeDatas2HTML.isPositiveInteger(config.options.values[i]) === false) throw new Error(errors.needPositiveInteger); } - this._pagination.options = + this._Pagination.options = { displayElement: { id:config.options.displayElement.id, eltDOM:checkContainerExist }, - name: (config.options.name) ? config.options.name : "Pagination :", + name: (config.options.name) ? config.options.name : "Pagination :", // idem, rendre obligatoire ? values:config.options.values }; } } - // Valeur de pagination par défaut qui peut être différente de celles éventuellement proposées en option : + // Valeur de Pagination par défaut qui doit faire partie des options proposées si elles existent : if(config.selectedValue !== undefined) { if(config.options !== undefined && (config.options.values.indexOf(config.selectedValue) === -1)) throw new Error(errors.needPaginationByDefaultBeInOptions); - if(freeDatas2HTML.isPositiveInteger(config.selectedValue)) - this._pagination.selectedValue=config.selectedValue; + if(FreeDatas2HTML.isPositiveInteger(config.selectedValue)) + this._Pagination.selectedValue=config.selectedValue; else throw new Error(errors.needPositiveInteger); } } - get pagination(): pagination + // Retourne les options de Pagination actuelles + get Pagination(): Pagination { - return this._pagination; + return this._Pagination; } - + + // Parse des données distantes (url) fournies en CSV : public async parse(): Promise { const converter=this; @@ -209,7 +210,7 @@ export class freeDatas2HTML realFields.push(results.meta.fields[i]); } results.meta.fields=realFields; - converter.parseMeta=results.meta; + converter.parseMetas=results.meta; resolve(true); }, error:function(error :any) @@ -224,7 +225,8 @@ export class freeDatas2HTML reject(new Error(errors.needUrl)); }); } - + + // Lance FreeDatas2HTML suivant les données reçues : public async run(): Promise { if (this._datasViewElt.eltDOM === undefined) @@ -234,122 +236,67 @@ export class freeDatas2HTML await this.parse(); - if(this.parseDatas.length === 0 || this.parseMeta!.fields === undefined) // je force avec "!", car l'existence de parseMeta est certaine après parse(). + if(this.parseDatas.length === 0 || this.parseMetas!.fields === undefined) // je force avec "!", car l'existence de parseMetas est certaine après parse(). throw new Error(errors.datasNotFound); else if(this.stopIfParseErrors && this.parseErrors.length!==0) console.error(this.parseErrors); else { let converter=this; - - // Si demandé, création des listes permettant de filter les données - if(this._datasSelectors.length > 0) + + // Si demandé, création d'une liste de valeurs de Pagination possibles + if(converter.Pagination !==undefined && converter.Pagination.options !==undefined && converter.Pagination.options.values.length > 0) { - let selectorsHTML : string [] = []; - for(let i in this._datasSelectors) - { - // Les colonnes devant servir de filtre existent-elles vraiment dans le fichier ? - if(this._datasSelectors[i].datasFieldNb > (this.parseMeta!.fields.length-1)) - throw new Error(errors.selectorFieldNotFound); - else - { - let values=[], colName=this.parseMeta!.fields[this._datasSelectors[i].datasFieldNb]; - for (let row in this.parseDatas) - { - if(this._datasSelectors[i].separator === undefined) - { - let checkedValue=this.parseDatas[row][colName].trim(); - // On ne garde pas les données vides (prévoir possible en option pour pouvoir sélectionner les données non classées sur cette colonne ?) - if(checkedValue !== "" && values.indexOf(checkedValue) === -1) - values.push(checkedValue); - } - else - { - let checkedValues=this.parseDatas[row][colName].split(this._datasSelectors[i].separator as string); - for(let i in checkedValues) - { - let checkedValue=checkedValues[i].trim(); - if(checkedValue !== "" && values.indexOf(checkedValue) === -1) - values.push(checkedValue); - } - } - } - if(values.length > 0) - { - if(this.getSortingFunctionForField(this._datasSelectors[i].datasFieldNb) !== undefined) - values.sort(this.getSortingFunctionForField(this._datasSelectors[i].datasFieldNb)!.sort); - else - values.sort(compare()); - this._datasSelectors[i].name=colName; - this._datasSelectors[i].values=values; - selectorsHTML[i]=""; - this._datasSelectors[i].eltDOM!.innerHTML=selectorsHTML[i]; - let selectElement = document.getElementById("freeDatas2HTMLSelector"+i) as HTMLInputElement; - selectElement.addEventListener('change', function(e) - { - converter.datasHTML=converter.createDatasHTML(converter.parseMeta!.fields as string[], converter.parseDatas); - converter.refreshView(); - }); - } - } - } - } - - // Si demandé, création d'une liste de paginations possibles - if(converter.pagination !==undefined && converter.pagination.options !==undefined && converter.pagination.options.values.length > 0) - { - const values=converter.pagination.options.values!; - let selectorsHTML=""; for(let j in values) selectorsHTML+=""; selectorsHTML+=""; - converter.pagination.options.displayElement.eltDOM!.innerHTML=selectorsHTML; + converter.Pagination.options.displayElement.eltDOM!.innerHTML=selectorsHTML; let selectElement = document.getElementById("freeDatas2HTMLPaginationSelector") as HTMLInputElement; - // Si une pagination par défaut existe et la sélectionne : - if(converter.pagination.selectedValue !== undefined) + // Si une Pagination par défaut existe, on la sélectionne : + if(converter.Pagination.selectedValue !== undefined) { - let indexSelectedValue=converter.pagination.options.values.indexOf(converter.pagination.selectedValue)+1; + let indexSelectedValue=converter.Pagination.options.values.indexOf(converter.Pagination.selectedValue)+1; selectElement.value=""+indexSelectedValue; } - selectElement.addEventListener('change', function(e) { if(selectElement.value === "0") - converter.pagination.selectedValue=undefined; + converter.Pagination.selectedValue=undefined; // = pas de Pagination else - converter.pagination.selectedValue=values[Number(selectElement.value)-1]; - converter.datasHTML=converter.createDatasHTML(converter.parseMeta!.fields as string[], converter.parseDatas); + converter.Pagination.selectedValue=values[Number(selectElement.value)-1]; + // on regénère le HTML : + converter.datasHTML=converter.createDatasHTML(converter.parseMetas!.fields as string[], converter.parseDatas); converter.refreshView(); }); } - // Je teste aussi les colonnes devant servir à classer les données. + // On teste aussi l'existence des champs devant servir à classer les données : for(let i in this._datasSortingColumns) { - if(this._datasSortingColumns[i].datasFieldNb > (this.parseMeta!.fields.length-1)) + if(this._datasSortingColumns[i].datasFieldNb > (this.parseMetas!.fields.length-1)) throw new Error(errors.sortingColumnsFieldNotFound); } // Si tout est ok, affichage initial de toutes les données du fichier - this.datasHTML=this.createDatasHTML(this.parseMeta!.fields, this.parseDatas); + this.datasHTML=this.createDatasHTML(this.parseMetas!.fields, this.parseDatas); this.refreshView(); return true; } } - private refreshView() : void + refreshView() : void { if(this._datasViewElt.eltDOM !== undefined) { const converter=this; this._datasViewElt.eltDOM.innerHTML=this.datasHTML; // Ici, car il faut que le tableau soit déjà dans le DOM pour "mettre sous écoute" les clics + // Pour éviter ce problème afficher séparément les "têtes de colonnes" qui ne bougent plus, des données qui peuvent être filtrées, paginées, etc. ? if(this._datasSortingColumns.length > 0) { - let getTableTh=document.querySelectorAll("table th"); + let getTableTh=document.getElementsByTagName("th"); if(getTableTh !== null) { for(let i in this._datasSortingColumns) @@ -368,7 +315,7 @@ export class freeDatas2HTML else converter.datasSortingColumns[i].order="desc"; converter._datasSortedColumn = converter.datasSortingColumns[i]; - converter.datasHTML=converter.createDatasHTML(converter.parseMeta!.fields as string[], converter.parseDatas); + converter.datasHTML=converter.createDatasHTML(converter.parseMetas!.fields as string[], converter.parseDatas); converter.refreshView(); }); } @@ -377,18 +324,8 @@ export class freeDatas2HTML } } - private createDatasHTML(fields: string[], datas: any[]) : string + createDatasHTML(fields: string[], datas: any[]) : string { - // Dois-je filtrer les données ? - let checkSelectorExist: HTMLSelectElement|null, filters: any[] = []; - for(let i in this._datasSelectors) - { - // Attention : je peux avoir des _datasSelectors fournis, mais pas de liste dans le DOM si aucune donnée ou autre problème. - checkSelectorExist=document.querySelector("#"+ this._datasSelectors[i].id+" select"); - if(checkSelectorExist != null && checkSelectorExist.selectedIndex != 0) - filters.push({ field: this._datasSelectors[i].name, value: this._datasSelectors[i].values![checkSelectorExist.selectedIndex-1], separator:this._datasSelectors[i].separator }); - } - // Dois-je classer les données par rapport à une colonne ? if(this._datasSortedColumn !== undefined) { @@ -404,11 +341,11 @@ export class freeDatas2HTML datas.sort( (a, b) => compare( {order: colOrder} )(a[col], b[col])); } - // Dois-je prendre en compte une pagination ? + // Dois-je prendre en compte une Pagination ? let firstData=0; - if (this.pagination !== undefined && this.pagination.selectedValue !== undefined && this.pagination.pages !== undefined && this.pagination.pages.selectedValue !== undefined) - firstData=this.pagination.selectedValue*(this.pagination.pages.selectedValue-1); - let maxData = (this.pagination !== undefined && this.pagination.selectedValue !== undefined) ? this.pagination.selectedValue : datas.length+1; + if (this.Pagination !== undefined && this.Pagination.selectedValue !== undefined && this.Pagination.pages !== undefined && this.Pagination.pages.selectedValue !== undefined) + firstData=this.Pagination.selectedValue*(this.Pagination.pages.selectedValue-1); + let maxData = (this.Pagination !== undefined && this.Pagination.selectedValue !== undefined) ? this.Pagination.selectedValue : datas.length+1; // Création du tableau de données : let datasHTML=""; @@ -419,34 +356,15 @@ export class freeDatas2HTML for (let row in datas) { let visible=true; - if(filters.length !== 0) + if(this.datasSelectors.length !== 0) { let i=0; - while(filters[i] !== undefined && visible===true) + while(this.datasSelectors[i] !== undefined && visible===true) { - // Il faut réutiliser le trim() utilisé pour créer les filtres, sinon on risque de ne pas retrouver certaines valeurs - if(filters[i].separator === undefined) - { - if(datas[row][filters[i].field].trim() !== filters[i].value) - visible=false; - } - else - { - visible=false; - let checkedValues=datas[row][filters[i].field].split(filters[i].separator as string); - for(let j in checkedValues) - { - if(checkedValues[j].trim() === filters[i].value) - { - visible=true; - break; - } - } - } + visible=this.datasSelectors[i].dataIsOk(datas[row]); // à revoir car cette fonction est nécessaire ! i++; } } - if(visible && nbTotal >= firstData && nbVisible < maxData) { datasHTML+=""; @@ -465,33 +383,33 @@ export class freeDatas2HTML } datasHTML+="
"; - // Si pagination définie et tous les enregistrements n'ont pas été affichés, alors création d'un sélecteur de pages - if (this.pagination !== undefined && this.pagination.selectedValue !== undefined && this.pagination.pages !== undefined && nbTotal > this.pagination.selectedValue) + // Si Pagination définie et tous les enregistrements n'ont pas été affichés, alors création d'un sélecteur de pages + if (this.Pagination !== undefined && this.Pagination.selectedValue !== undefined && this.Pagination.pages !== undefined && nbTotal > this.Pagination.selectedValue) { - let nbPages=Math.ceil(nbTotal/this.pagination.selectedValue); - let selectorsHTML=""; + this.Pagination.pages.values=[1]; for(let j=2; j <= nbPages; j++) { selectorsHTML+=""; - this.pagination.pages.values.push(j); + this.Pagination.pages.values.push(j); } selectorsHTML+=""; - this.pagination.pages.displayElement.eltDOM!.innerHTML=selectorsHTML; + this.Pagination.pages.displayElement.eltDOM!.innerHTML=selectorsHTML; let selectElement = document.getElementById("freeDatas2HTMLPagesSelector") as HTMLInputElement; - if(this.pagination.pages.selectedValue !== undefined) - selectElement.value=""+this.pagination.pages.selectedValue; + if(this.Pagination.pages.selectedValue !== undefined) + selectElement.value=""+this.Pagination.pages.selectedValue; let converter=this; - this.pagination.pages.selectedValue=1; + this.Pagination.pages.selectedValue=1; selectElement.addEventListener('change', function(e) { - converter.pagination.pages!.selectedValue=Number(selectElement.value); - converter.datasHTML=converter.createDatasHTML(converter.parseMeta!.fields as string[], converter.parseDatas); + converter.Pagination.pages!.selectedValue=Number(selectElement.value); + converter.datasHTML=converter.createDatasHTML(converter.parseMetas!.fields as string[], converter.parseDatas); converter.refreshView(); }); } - else if(this.pagination !== undefined && this.pagination.pages !== undefined) - this.pagination.pages.displayElement.eltDOM!.innerHTML=""; + else if(this.Pagination !== undefined && this.Pagination.pages !== undefined) + this.Pagination.pages.displayElement.eltDOM!.innerHTML=""; return datasHTML; } diff --git a/src/freeDatas2HTMLInterfaces.ts b/src/freeDatas2HTMLInterfaces.ts index b9141b7..b6fbfbb 100644 --- a/src/freeDatas2HTMLInterfaces.ts +++ b/src/freeDatas2HTMLInterfaces.ts @@ -1,37 +1,40 @@ -export interface domElement +export interface DOMElement { id: string; eltDOM?: HTMLElement; } -export interface selectors extends domElement // revoir pour donner un autre nom +export interface Selectors { - datasFieldNb: number; - separator?: string; + datasViewElt: DOMElement; + datasFieldNb: number|undefined; + separator?: string|undefined; name?: string; values?: string[]; + selector2HTML() : void; + dataIsOk(data: any) : boolean; } -export interface sortingColumns +export interface SortingColumns { datasFieldNb: number; order?: "asc"|"desc"|undefined; } -export interface sortingFunctions +export interface SortingFunctions { datasFieldNb: number; sort(a: any,b: any, order?: "asc"|"desc"): number; // cf. https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Array/sort } -export interface pagination +export interface Pagination { options?: { - displayElement: domElement; + displayElement: DOMElement; name?: string; // rendre obligatoire ? values: number[]; }; - selectedValue?: number; // on peut utiliser une pagination sans proposer d'options à l'utilisateur. + selectedValue?: number; // on peut utiliser une Pagination sans proposer d'options à l'utilisateur. pages?: { - displayElement: domElement; + displayElement: DOMElement; name?: string; // rendre obligatoire ? values?: number[]; selectedValue?: number; diff --git a/src/freeDatas2HTMLSelector.ts b/src/freeDatas2HTMLSelector.ts new file mode 100644 index 0000000..8889e6c --- /dev/null +++ b/src/freeDatas2HTMLSelector.ts @@ -0,0 +1,161 @@ +const { compare }= require('natural-orderby'); +const errors = require("./errors.js"); +import { DOMElement, Selectors } from "./freeDatas2HTMLInterfaces"; +import { FreeDatas2HTML } from "./freeDatas2HTML"; + +export class Selector implements Selectors +{ + _converter: FreeDatas2HTML; + _datasViewElt: DOMElement= { id:"", eltDOM:undefined }; // élément du DOM dans lequel afficher le "select" + _datasFieldNb: number|undefined; // numéro du champ dont les données serviront au filtre + _separator: string|undefined; // séparateur éventuel pour les données du champ + name: string = ""; // nom à afficher dans le DOM comme "label" du "select" + values: string[]=[]; // données proposées par le filtre, après traitement des données reçues + + constructor(converter: FreeDatas2HTML) + { + if(converter.parseMetas === undefined || converter.parseMetas.fields === undefined || converter.parseDatas.length === 0) + throw new Error(errors.selectorNeedDatas); + else + this._converter=converter; + } + + // Vérifie que l'élément devant recevoir le filtre existe dans la page + set datasViewElt(elt: DOMElement) + { + let checkContainerExist=document.getElementById(elt.id); + if(checkContainerExist === null) + throw new Error(errors.elementNotFound+elt.id); + else + { + this._datasViewElt.id=elt.id; + this._datasViewElt.eltDOM=checkContainerExist; + } + } + + get datasViewElt() : DOMElement + { + return this._datasViewElt; + } + + // Vérifie que le numéro de champ existe dans les données reçues + set datasFieldNb(datasFieldNb: number|undefined) + { + if(datasFieldNb !== undefined && this._converter.parseMetas!.fields![datasFieldNb] === undefined) + throw new Error(errors.selectorFieldNotFound); + else + this._datasFieldNb=datasFieldNb; + } + + get datasFieldNb() : number|undefined + { + return this._datasFieldNb; + } + + // Ignore un séparateur de données vide + // Attention : pas de trim() ici, car l'espace peut être un séparateur + set separator(separator: string|undefined) + { + if(separator === "") + this._separator=undefined; + else + this._separator=separator; + } + + // Création du "; // l'option zéro permet de rafraichir en ignorant ce filtre + for(let j in this.values) + selectorsHTML+=""; + selectorsHTML+=""; + this. _datasViewElt.eltDOM.innerHTML=selectorsHTML; + const selectElement=document.getElementById("freeDatas2HTML_"+this._datasViewElt.id) as HTMLInputElement, mySelector=this; + selectElement.addEventListener("change", function(e) + { + mySelector._converter.datasHTML=mySelector._converter.createDatasHTML(mySelector._converter.parseMetas!.fields!, mySelector._converter.parseDatas); + mySelector._converter.refreshView(); + }); + } + } + } + + // Vérifie sur une valeur est sélectionnée dans la liste et la retourne + public getSelectionnedId() : number + { + const selectElement=document.getElementById("freeDatas2HTML_"+this._datasViewElt.id) as HTMLInputElement; + if(selectElement === undefined) + return 0; + else + return parseInt(selectElement.value,10); + } + + // Vérifie sur l'enregistrement est valide pour la valeur sélectionnée dans le filtre + public dataIsOk(data: any) : boolean + { + if(this.name === undefined || data[this.name] === undefined) // attribut absent pour cet enregistrement + return false; + + let selectedValue = this.getSelectionnedId(); + if(selectedValue === 0) // = pas de valeur sélectionnée = pas de filtre sur ce champ + return true; + else + selectedValue=selectedValue-1; + + if(this.values[selectedValue] === undefined) + throw new Error(errors.selectorSelectedIndexNotFound); + + const selectedValueTxt=this.values[selectedValue] ; + if(this._separator === undefined) + { + if(data[this.name].trim() !== selectedValueTxt) + return false; + else + return true; + } + else + { + let find=false; + let checkedValues=String(data[this.name]).split(this._separator); + for(let j in checkedValues) + { + if(checkedValues[j].trim() === selectedValueTxt) + { + find=true; + break; + } + } + return find; + } + } +} \ No newline at end of file diff --git a/tests/fixtures.js b/tests/fixtures.js index ed47fc0..6de4e84 100644 --- a/tests/fixtures.js +++ b/tests/fixtures.js @@ -2,13 +2,14 @@ module.exports = { datasViewEltHTML: '
', datasHTML : '
Z (numéro atomique)ÉlémentSymboleFamilleAbondance des éléments dans la croûte terrestre (μg/k)
1HydrogèneHNon-métal> 100000
2HéliumHeGaz noble> 1 et < 100 000
3LithiumLiMétal alcalin> 1 et < 100 000
4BérylliumBeMétal alcalino-terreux> 1 et < 100 000
5BoreBMétalloïde> 1 et < 100 000
6CarboneCNon-métal> 100000
7AzoteNNon-métal> 1 et < 100 000
8OxygèneONon-métal> 100000
9FluorFHalogène> 100000
10NéonNeGaz noble> 1 et < 100 000
11SodiumNaMétal alcalin> 100000
12MagnésiumMgMétal alcalino-terreux> 100000
13AluminiumAlMétal pauvre> 100000
14SiliciumSiMétalloïde> 100000
15PhosphorePNon-métal> 100000
16SoufreSNon-métal> 100000
17ChloreClHalogène> 100000
18ArgonArGaz noble> 1 et < 100 000
19PotassiumKMétal alcalin> 100000
20CalciumCaMétal alcalino-terreux> 100000
21ScandiumScMétal de transition> 1 et < 100 000
22TitaneTiMétal de transition> 100000
23VanadiumVMétal de transition> 100000
24ChromeCrMétal de transition> 100000
25ManganèseMnMétal de transition> 100000
26FerFeMétal de transition> 100000
27CobaltCoMétal de transition> 1 et < 100 000
28NickelNiMétal de transition> 1 et < 100 000
29CuivreCuMétal de transition> 1 et < 100 000
30ZincZnMétal pauvre> 1 et < 100 000
31GalliumGaMétal pauvre> 1 et < 100 000
32GermaniumGeMétalloïde> 1 et < 100 000
33ArsenicAsMétalloïde> 1 et < 100 000
34SéléniumSeNon-métal> 1 et < 100 000
35BromeBrHalogène> 1 et < 100 000
36KryptonKrgaz rare≤ 1
37RubidiumRbMétal alcalin> 1 et < 100 000
38StrontiumSrMétal alcalino-terreux> 100000
39YttriumYMétal de transition> 1 et < 100 000
40ZirconiumZrMétal de transition> 100000
41NiobiumNbMétal de transition> 1 et < 100 000
42MolybdèneMoMétal de transition> 1 et < 100 000
43TechnétiumTcMétal de transitionTraces
44RuthéniumRuMétal de transition≤ 1
45RhodiumRhMétal de transition≤ 1
46PalladiumPdMétal de transition> 1 et < 100 000
47ArgentAgMétal de transition> 1 et < 100 000
48CadmiumCdMétal pauvre> 1 et < 100 000
49IndiumInMétal pauvre> 1 et < 100 000
50ÉtainSnMétal pauvre> 1 et < 100 000
51AntimoineSbMétalloïde> 1 et < 100 000
52TellureTeMétalloïde≤ 1
53IodeIHalogène> 1 et < 100 000
54XénonXegaz rare≤ 1
55CésiumCsMétal alcalin> 1 et < 100 000
56BaryumBaMétal alcalino-terreux> 100000
57LanthaneLaLanthanide> 1 et < 100 000
58CériumCeLanthanide> 1 et < 100 000
59PraséodymePrLanthanide> 1 et < 100 000
60NéodymeNdLanthanide> 1 et < 100 000
61ProméthiumPmLanthanideTraces
62SamariumSmLanthanide> 1 et < 100 000
63EuropiumEuLanthanide> 1 et < 100 000
64GadoliniumGdLanthanide> 1 et < 100 000
65TerbiumTbLanthanide> 1 et < 100 000
66DysprosiumDyLanthanide> 1 et < 100 000
67HolmiumHoLanthanide> 1 et < 100 000
68ErbiumErLanthanide> 1 et < 100 000
69ThuliumTmLanthanide> 1 et < 100 000
70YtterbiumYbLanthanide> 1 et < 100 000
71LutéciumLuLanthanide> 1 et < 100 000
72HafniumHfMétal de transition> 1 et < 100 000
73TantaleTaMétal de transition> 1 et < 100 000
74TungstèneWMétal de transition> 1 et < 100 000
75RhéniumReMétal de transition≤ 1
76OsmiumOsMétal de transition> 1 et < 100 000
77IridiumIrMétal de transition≤ 1
78PlatinePtMétal de transition> 1 et < 100 000
79OrAuMétal de transition> 1 et < 100 000
80MercureHgMétal pauvre> 1 et < 100 000
81ThalliumTlMétal pauvre> 1 et < 100 000
82PlombPbMétal pauvre> 1 et < 100 000
83BismuthBiMétal pauvre> 1 et < 100 000
84PoloniumPoMétal pauvre≤ 1
85AstateAtMétalloïdeTraces
86RadonRnGaz noble≤ 1
87FranciumFrMétal alcalinTraces
88RadiumRaMétal alcalino-terreux≤ 1
89ActiniumAcActinide≤ 1
90ThoriumThActinide> 1 et < 100 000
91ProtactiniumPaActinide≤ 1
92UraniumUActinide> 1 et < 100 000
93NeptuniumNpActinideTraces
94PlutoniumPuActinideTraces
95AmériciumAmActinideInexistant
96CuriumCmActinideInexistant
97BerkéliumBkActinideInexistant
98CaliforniumCfActinideInexistant
99EinsteiniumEsActinideInexistant
100FermiumFmActinideInexistant
101MendéléviumMdActinideInexistant
102NobéliumNoActinideInexistant
103LawrenciumLrActinideInexistant
104RutherfordiumRfMétal de transitionInexistant
105DubniumDbMétal de transitionInexistant
106SeaborgiumSgMétal de transitionInexistant
107BohriumBhMétal de transitionInexistant
108HassiumHsMétal de transitionInexistant
109MeitnériumMtIndéfinieInexistant
110DarmstadtiumDsIndéfinieInexistant
111RoentgeniumRgIndéfinieInexistant
112CoperniciumCnMétal de transitionInexistant
113NihoniumNhIndéfinieInexistant
114FléroviumFlIndéfinieInexistant
115MoscoviumMcIndéfinieInexistant
116LivermoriumLvIndéfinieInexistant
117TennesseTsIndéfinieInexistant
118OganessonOgIndéfinieInexistant
', - selector1HTML: '', - selector2HTML: '', - selector1HTMLWithTags: '', - datasHTMLFor1Select: '
Z (numéro atomique)ÉlémentSymboleFamilleAbondance des éléments dans la croûte terrestre (μg/k)
9FluorFHalogène> 100000
17ChloreClHalogène> 100000
35BromeBrHalogène> 1 et < 100 000
53IodeIHalogène> 1 et < 100 000
', + selector1HTML: '', + selector2HTML: '', + selector1HTMLWithSeparator: '', + selector1HTMLWithFunction: '', + datasHTMLFor1Select: '
Z (numéro atomique)ÉlémentSymboleFamilleAbondance des éléments dans la croûte terrestre (μg/k)
9FluorFHalogène> 100000
17ChloreClHalogène> 100000
35BromeBrHalogène> 1 et < 100 000
53IodeIHalogène> 1 et < 100 000
', datasHTMLFor2Select:'
Z (numéro atomique)ÉlémentSymboleFamilleAbondance des éléments dans la croûte terrestre (μg/k)
2HéliumHeGaz noble> 1 et < 100 000
10NéonNeGaz noble> 1 et < 100 000
18ArgonArGaz noble> 1 et < 100 000
', datasHTMLFor2SelectNone:'
Z (numéro atomique)ÉlémentSymboleFamilleAbondance des éléments dans la croûte terrestre (μg/k)
', - datasHTMLForSelectTagsField:'
Z (numéro atomique)ÉlémentSymboleFamilleAbondance des éléments dans la croûte terrestre (μg/k)Étiquettes
118OganessonOgIndéfinieInexistantExemple10
', + datasHTMLForSelectTagsField:'
Z (numéro atomique)ÉlémentSymboleFamilleAbondance des éléments dans la croûte terrestre (μg/k)Étiquettes
118OganessonOgIndéfinieInexistantExemple10
', sortingColumn1HTML: 'Z (numéro atomique)', sortingColumn2HTML: 'Symbole', datasHTMLFor2Select1Clic: '
Z (numéro atomique)ÉlémentSymboleFamilleAbondance des éléments dans la croûte terrestre (μg/k)
18ArgonArGaz noble> 1 et < 100 000
2HéliumHeGaz noble> 1 et < 100 000
10NéonNeGaz noble> 1 et < 100 000
', diff --git a/tests/freeDatas2HTMLSpec.ts b/tests/freeDatas2HTMLSpec.ts index f223f82..4e2c92d 100644 --- a/tests/freeDatas2HTMLSpec.ts +++ b/tests/freeDatas2HTMLSpec.ts @@ -1,14 +1,14 @@ -import { freeDatas2HTML } from "../src/freeDatas2HTML"; +import { FreeDatas2HTML, Selector } from "../src/freeDatas2HTML"; const errors=require("../src/errors.js"); const fixtures=require("./fixtures.js"); -describe("freeDatas2HTML", () => +describe("Test du script central de FreeDatas2HTML", () => { - let converter: freeDatas2HTML; + let converter: FreeDatas2HTML; beforeEach( () => { - converter=new freeDatas2HTML(); + converter=new FreeDatas2HTML(); document.body.insertAdjacentHTML('afterbegin', fixtures.datasViewEltHTML); }); @@ -17,9 +17,9 @@ describe("freeDatas2HTML", () => document.body.removeChild(document.getElementById('fixture')); }); - it("Doit avoir créé une instance de freeDatas2HTML", () => + it("Doit avoir créé une instance de FreeDatas2HTML", () => { - expect(converter).toBeInstanceOf(freeDatas2HTML); + expect(converter).toBeInstanceOf(FreeDatas2HTML); }); describe("Test des données de configuration reçues.", () => @@ -41,38 +41,18 @@ describe("freeDatas2HTML", () => it("Doit retourner un booléen indiquant si un nombre est naturel ou non.", () => { - expect(freeDatas2HTML.isNaturalNumber(-1)).toBeFalse(); - expect(freeDatas2HTML.isNaturalNumber(1.25)).toBeFalse(); - expect(freeDatas2HTML.isNaturalNumber(0)).toBeTrue(); - expect(freeDatas2HTML.isNaturalNumber(1)).toBeTrue(); + expect(FreeDatas2HTML.isNaturalNumber(-1)).toBeFalse(); + expect(FreeDatas2HTML.isNaturalNumber(1.25)).toBeFalse(); + expect(FreeDatas2HTML.isNaturalNumber(0)).toBeTrue(); + expect(FreeDatas2HTML.isNaturalNumber(1)).toBeTrue(); }); it("Doit retourner un booléen indiquant si un nombre est un entier positif ou non.", () => { - expect(freeDatas2HTML.isPositiveInteger(-1)).toBeFalse(); - expect(freeDatas2HTML.isPositiveInteger(1.25)).toBeFalse(); - expect(freeDatas2HTML.isPositiveInteger(0)).toBeFalse(); - expect(freeDatas2HTML.isPositiveInteger(1)).toBeTrue(); - }); - - // Filtres : - it("Ne doit accepter que les sélecteurs pour lesquels un élément a été trouvé dans la page pour l'id fourni.", () => - { - converter.datasSelectors=[{ datasFieldNb:2, id:"selector2" },{ datasFieldNb:3, id:"selector3" }]; - expect(converter.datasSelectors.length).toEqual(1); - expect(converter.datasSelectors[0].id).toEqual("selector2"); - }); - - it("Si un séparateur vide est fourni pour un sélecteur, il doit être ignoré.", () => - { - converter.datasSelectors=[{ datasFieldNb:2, id:"selector2", separator:"" }]; - expect(converter.datasSelectors[0].separator).toBeUndefined(); - }); - - it("Doit accepter tous les sélecteurs si leurs informations sont valides.", () => - { - converter.datasSelectors=[{ datasFieldNb:0, id:"selector1" },{ datasFieldNb:3, id:"selector2" }]; - expect(converter.datasSelectors.length).toEqual(2); + expect(FreeDatas2HTML.isPositiveInteger(-1)).toBeFalse(); + expect(FreeDatas2HTML.isPositiveInteger(1.25)).toBeFalse(); + expect(FreeDatas2HTML.isPositiveInteger(0)).toBeFalse(); + expect(FreeDatas2HTML.isPositiveInteger(1)).toBeTrue(); }); // Classement des données : @@ -96,45 +76,45 @@ describe("freeDatas2HTML", () => // Pagination : it("Doit générer une erreur quand aucun élément n'est fourni pour recevoir le sélecteur de pages, alors que cela est nécessaire.", () => { - expect(() => { return converter.pagination={ selectedValue:10 }; }).toThrowError(errors.needPagesSelectorElt); - expect(() => { return converter.pagination={ options: { displayElement: { id:"paginationOptions" }, values: [10,20] }}; }).toThrowError(errors.needPagesSelectorElt); + expect(() => { return converter.Pagination={ selectedValue:10 }; }).toThrowError(errors.needPagesSelectorElt); + expect(() => { return converter.Pagination={ options: { displayElement: { id:"paginationOptions" }, values: [10,20] }}; }).toThrowError(errors.needPagesSelectorElt); }); it("Doit générer une erreur si l'élément fourni pour recevoir le sélecteur de pages n'existe pas dans le DOM.", () => { - expect(() => { return converter.pagination={ selectedValue:10, pages: { displayElement: { id:"dontExist" }} }; }).toThrowError(errors.elementNotFound+"dontExist"); + expect(() => { return converter.Pagination={ selectedValue:10, pages: { displayElement: { id:"dontExist" }} }; }).toThrowError(errors.elementNotFound+"dontExist"); }); - it("Doit générer une erreur si l'élément fourni pour recevoir le sélecteur de pagination n'existe pas dans le DOM.", () => + it("Doit générer une erreur si l'élément fourni pour recevoir le sélecteur de Pagination n'existe pas dans le DOM.", () => { - expect(() => { return converter.pagination={ options: { displayElement: { id:"dontExist" }, values: [10,20] }, pages: { displayElement: { id:"pages" }}}; }).toThrowError(errors.elementNotFound+"dontExist"); + expect(() => { return converter.Pagination={ options: { displayElement: { id:"dontExist" }, values: [10,20] }, pages: { displayElement: { id:"pages" }}}; }).toThrowError(errors.elementNotFound+"dontExist"); }); - it("Doit générer une erreur si au moins une des options de pagination proposée n'est pas un entier positif.", () => + it("Doit générer une erreur si au moins une des options de Pagination proposée n'est pas un entier positif.", () => { - expect(() => { return converter.pagination={ options: { displayElement: { id:"paginationOptions" }, values:[0,10,20] }, pages: { displayElement: { id:"pages" }}}; }).toThrowError(errors.needPositiveInteger); + expect(() => { return converter.Pagination={ options: { displayElement: { id:"paginationOptions" }, values:[0,10,20] }, pages: { displayElement: { id:"pages" }}}; }).toThrowError(errors.needPositiveInteger); }); - it("Doit générer une erreur si la pagination par défaut n'est pas un entier positif.", () => + it("Doit générer une erreur si la Pagination par défaut n'est pas un entier positif.", () => { - expect(() => { return converter.pagination={ selectedValue:0, pages: { displayElement: { id:"pages" }} }; }).toThrowError(errors.needPositiveInteger); + expect(() => { return converter.Pagination={ selectedValue:0, pages: { displayElement: { id:"pages" }} }; }).toThrowError(errors.needPositiveInteger); }); - it("Doit générer une erreur si la pagination par défaut ne fait pas partie des valeurs proposées en option.", () => + it("Doit générer une erreur si la Pagination par défaut ne fait pas partie des valeurs proposées en option.", () => { - expect(() => { return converter.pagination={ selectedValue:15, options: { displayElement: { id:"paginationOptions" }, values:[10,20,50] }, pages: { displayElement: { id:"pages" }}}; }).toThrowError(errors.needPaginationByDefaultBeInOptions); + expect(() => { return converter.Pagination={ selectedValue:15, options: { displayElement: { id:"paginationOptions" }, values:[10,20,50] }, pages: { displayElement: { id:"pages" }}}; }).toThrowError(errors.needPaginationByDefaultBeInOptions); }); - it("Doit accepter une configuration correcte pour la pagination.", () => + it("Doit accepter une configuration correcte pour la Pagination.", () => { - let paginationOk = + let PaginationOk = { selectedValue:10, options: { displayElement : { id:"paginationOptions" }, values: [10,20,50], - name: "Choix de pagination :" + name: "Choix de Pagination :" }, pages: { @@ -142,7 +122,7 @@ describe("freeDatas2HTML", () => name: "Page à afficher :" } }; - expect(() => { return converter.pagination=paginationOk; }).not.toThrowError(); + expect(() => { return converter.Pagination=PaginationOk; }).not.toThrowError(); }); }); @@ -168,7 +148,7 @@ describe("freeDatas2HTML", () => converter.datasViewElt={ id:"datas" }; converter.datasSourceUrl="http://localhost:9876/datas/datas-errors2.csv"; await converter.parse(); - expect(converter.parseMeta.fields.length).toEqual(5); + expect(converter.parseMetas.fields.length).toEqual(5); }); it("Ne doit enregistrer aucune erreur de lecture si le fichier est ok.", async () => @@ -231,115 +211,6 @@ describe("freeDatas2HTML", () => }); }); - describe("Création et action des sélecteurs permettant de filter les données affichées.", () => - { - beforeEach( () => - { - converter.datasViewElt={ id:"datas" }; - converter.datasSourceUrl="http://localhost:9876/datas/datas1.csv"; - }); - - it("Doit générer une erreur si au moins un des numéros de colonne fournis pour les sélecteurs ne correspond pas à une des colonne du fichier.", async () => - { - converter.datasSelectors=[{ datasFieldNb:0, id:"selector1"},{ datasFieldNb:5, id:"selector2"}]; // il y a bien 5 champs, mais la numérotation commence à 0 :-) - await expectAsync(converter.run()).toBeRejectedWith(new Error(errors.selectorFieldNotFound)); - }); - - it("Ne doit pas pas générer d'erreur si tous les numéros de colonne des sélecteurs correspondent à une des colonnes du fichier.", async () => - { - converter.datasSelectors=[{ datasFieldNb:3, id:"selector1"},{ datasFieldNb:4, id:"selector2"}]; - await expectAsync(converter.run()).not.toBeRejected(); - }); - - it("Pour chaque sélecteur demandé, doit générer un élement listant les valeurs distinctes du champ spécifié, classées dans le bon ordre.", () => + { + selector.datasViewElt={ id:"selector1"}; + selector.datasFieldNb=3; + selector.selector2HTML(); + expect(document.getElementById("selector1").innerHTML).toEqual(fixtures.selector1HTML); + selector=new Selector(converter); + selector.datasViewElt={ id:"selector2"}; + selector.datasFieldNb=4; + selector.selector2HTML(); + expect(document.getElementById("selector2").innerHTML).toEqual(fixtures.selector2HTML); + }); + + it("Si des valeurs vides sont présentes dans une champ utilisé pour un sélecteur, elles doivent être ignorées.", async () => + { + converter.datasSourceUrl="http://localhost:9876/datas/datas1-emtyinfield.csv"; + await converter.parse(); + selector.datasViewElt={ id:"selector1"}; + selector.datasFieldNb=3; + selector.selector2HTML(); + expect(document.getElementById("selector1").innerHTML).toEqual(fixtures.selector1HTML); + }); + + it("Si un séparateur est fourni, les valeurs distinctes extraites de ce champ doivent le prendre en compte.", async () => + { + converter.datasSourceUrl="http://localhost:9876/datas/datas1+tagsfield.csv"; + await converter.parse(); + selector.datasViewElt={ id:"selector1"}; + selector.datasFieldNb=5; + selector.separator="|"; + selector.selector2HTML(); + expect(document.getElementById("selector1").innerHTML).toEqual(fixtures.selector1HTMLWithSeparator); + }); + + it("Si une fonction spécifique est fournie pour le champ utilisé pour ce filtre, elle doit être prise en compte.", () => + { + const mySort=(a: any, b: any, order: "asc"|"desc"="asc") => + { + const values=[ "> 100000", "> 1 et < 100 000", "≤ 1", "Traces", "Inexistant"]; + if(order === "desc") + values.reverse(); + if(values.indexOf(a) > values.indexOf(b)) + return -1; + else if(values.indexOf(a) < values.indexOf(b)) + return 1; + else + return 0; + }; + converter.datasSortingFunctions=[{ datasFieldNb: 4, sort:mySort }]; + selector.datasViewElt={ id:"selector1"}; + selector.datasFieldNb=4; + selector.separator="|"; + selector.selector2HTML(); + expect(document.getElementById("selector1").innerHTML).toEqual(fixtures.selector1HTMLWithFunction); + }); + + it("Doit retourner false, si une donnée testée ne correspond pas à la valeur sélectionnée dans la liste.", () => + { + selector.datasViewElt={ id:"selector1"}; + selector.datasFieldNb=3; + selector.selector2HTML(); + let selectElement=document.getElementById("freeDatas2HTML_selector1") as HTMLInputElement; + selectElement.value="4"; + let data2Test= { + "Z (numéro atomique)" : "53", + "Élément": "Iode", + "Symbole": "I", + "Famille": "Halogene", // manque un accent :) + "Abondance des éléments dans la croûte terrestre (μg/k)": "> 1 et < 100 000", + }; + expect(selector.dataIsOk(data2Test)).toBeFalse(); + let data2Test2= { // le champ à filtrer est manquant + "Z (numéro atomique)" : "53", + "Élément": "Iode", + "Symbole": "I", + "Abondance des éléments dans la croûte terrestre (μg/k)": "> 1 et < 100 000", + }; + expect(selector.dataIsOk(data2Test2)).toBeFalse(); + }); + + it("Doit retourner true, si une donnée testée correspond pas à la valeur sélectionnée dans la liste.", () => + { + selector.datasViewElt={ id:"selector1"}; + selector.datasFieldNb=3; + selector.selector2HTML(); + let selectElement=document.getElementById("freeDatas2HTML_selector1") as HTMLInputElement; + selectElement.value="4"; + let data2Test= { + "Z (numéro atomique)" : "53", + "Élément": "Iode", + "Symbole": "I", + "Famille": "Halogène", + "Abondance des éléments dans la croûte terrestre (μg/k)": "> 1 et < 100 000", + }; + expect(selector.dataIsOk(data2Test)).toBeTrue(); + }); + + it("Doit toujours retourner true, si aucune valeur sélectionnée dans la liste.", () => + { + selector.datasViewElt={ id:"selector1"}; + selector.datasFieldNb=3; + selector.selector2HTML(); + let data2Test= { + "Z (numéro atomique)" : "530", + "Élément": "Yode", + "Symbole": "L", + "Famille": "Halogene", + "Abondance des éléments": "> 1 et < 100 000", + }; + expect(selector.dataIsOk(data2Test)).toBeTrue(); + }); + }); + + describe("Action des sélecteurs en corrélation avec le convertisseur.", () => + { + beforeEach( async () => + { + document.body.insertAdjacentHTML("afterbegin", fixtures.datasViewEltHTML); + converter=new FreeDatas2HTML(); + converter.datasViewElt={ id:"datas" }; + converter.datasSourceUrl="http://localhost:9876/datas/datas1.csv"; + await converter.parse(); + selector=new Selector(converter); + }); + + it("Le choix d'une option dans un des sélecteurs doit modifier le contenu du tableau pour ne garder que les données correspondantes et les afficher toutes si sélection 0.", () => + { + selector.datasViewElt={ id:"selector1"}; + selector.datasFieldNb=3; + selector.selector2HTML(); + converter.datasSelectors=[selector]; + let selectElement=document.getElementById("freeDatas2HTML_selector1") as HTMLInputElement; + selectElement.value="4"; + selectElement.dispatchEvent(new Event('change')); + let txtDatasViewsElt=document.getElementById("datas").innerHTML; + expect(txtDatasViewsElt).toEqual(fixtures.datasHTMLFor1Select); + selectElement.value="0"; + selectElement.dispatchEvent(new Event('change')); + txtDatasViewsElt=document.getElementById("datas").innerHTML; + expect(txtDatasViewsElt).toEqual(fixtures.datasHTML); + }); + + it("Si plusieurs sélecteurs sont utilisés, seules les données correspondant à tous ces choix doivent être affichées. Il peut n'y avoir aucun résultat.", () => + { + selector.datasViewElt={ id:"selector1"}; + selector.datasFieldNb=3; + selector.selector2HTML(); + let selector2=new Selector(converter); + selector2.datasViewElt={ id:"selector2"}; + selector2.datasFieldNb=4; + selector2.selector2HTML(); + converter.datasSelectors=[selector, selector2]; + let selectElement=document.getElementById("freeDatas2HTML_selector1") as HTMLInputElement; + selectElement.value="2"; + selectElement=document.getElementById("freeDatas2HTML_selector2") as HTMLInputElement; + selectElement.value="1"; + selectElement.dispatchEvent(new Event('change')); + let txtDatasViewsElt=document.getElementById("datas").innerHTML; + expect(txtDatasViewsElt).toEqual(fixtures.datasHTMLFor2Select); + selectElement.value="4"; + selectElement.dispatchEvent(new Event('change')); + txtDatasViewsElt=document.getElementById("datas").innerHTML; + expect(txtDatasViewsElt).toEqual(fixtures.datasHTMLFor2SelectNone); + }); + + it("Si un séparateur est fourni pour un sélecteur, lorsque qu'une valeur y est sélectionnée, toutes les lignes de données la contenant doivent être affichées.", async () => + { + converter.datasSourceUrl="http://localhost:9876/datas/datas1+tagsfield.csv"; + await converter.parse(); + selector.datasViewElt={ id:"selector1"}; + selector.datasFieldNb=5; + selector.separator="|"; + selector.selector2HTML(); + converter.datasSelectors=[selector]; + let selectElement=document.getElementById("freeDatas2HTML_selector1") as HTMLInputElement; + selectElement.value="11"; //="Exemple10" retournant une seule ligne + selectElement.dispatchEvent(new Event('change')); + let txtDatasViewsElt=document.getElementById("datas").innerHTML; + expect(txtDatasViewsElt).toEqual(fixtures.datasHTMLForSelectTagsField); + }); + + it("Les sélecteurs basés sur un séparateur peuvent fonctionner avec un autre filtre.", async () => + { + converter.datasSourceUrl="http://localhost:9876/datas/datas1+tagsfield.csv"; + await converter.parse(); + selector.datasViewElt={ id:"selector1"}; + selector.datasFieldNb=4; + selector.selector2HTML(); + let selector2=new Selector(converter); + selector2.datasViewElt={ id:"selector2"}; + selector2.datasFieldNb=5; + selector2.separator="|"; + selector2.selector2HTML(); + converter.datasSelectors=[selector, selector2]; + let selectElement=document.getElementById("freeDatas2HTML_selector2") as HTMLInputElement; + selectElement.value="11"; //="Exemple10" retournant une seule ligne + selectElement.dispatchEvent(new Event('change')); + let txtDatasViewsElt=document.getElementById("datas").innerHTML; + expect(txtDatasViewsElt).toEqual(fixtures.datasHTMLForSelectTagsField); + selectElement=document.getElementById("freeDatas2HTML_selector1") as HTMLInputElement; + selectElement.value="1"; // doit garder la ligne + selectElement.dispatchEvent(new Event('change')); + expect(txtDatasViewsElt).toEqual(fixtures.datasHTMLForSelectTagsField); + selectElement.value="3"; // doit supprimer la ligne restant + selectElement.dispatchEvent(new Event('change')); + txtDatasViewsElt=document.getElementById("datas").innerHTML; + expect(txtDatasViewsElt).toEqual("
Z (numéro atomique)ÉlémentSymboleFamilleAbondance des éléments dans la croûte terrestre (μg/k)Étiquettes
"); + }); + }); + + +}); \ No newline at end of file