Séparation du code générant les filtres, avec classe et script de test dédiés + injection dans la classe principale.
This commit is contained in:
parent
8ae9aeb5e8
commit
aa2a3414b2
@ -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.",
|
||||
};
|
@ -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 <pagination>this._pagination;
|
||||
return <Pagination>this._Pagination;
|
||||
}
|
||||
|
||||
|
||||
// Parse des données distantes (url) fournies en CSV :
|
||||
public async parse(): Promise<any>
|
||||
{
|
||||
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<any>
|
||||
{
|
||||
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]="<label for='freeDatas2HTMLSelector"+i+"'>"+colName+" : </label><select name='freeDatas2HTMLSelector"+i+"' id='freeDatas2HTMLSelector"+i+"'><option value='0'>----</option>";
|
||||
for(let j in values)
|
||||
selectorsHTML[i]+="<option value='"+(Number(j)+1)+"'>"+values[j]+"</option>";
|
||||
selectorsHTML[i]+="</select>";
|
||||
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="<label for='freeDatas2HTMLPaginationSelector'>"+converter.pagination.options.name+" </label><select name='freeDatas2HTMLPaginationSelector' id='freeDatas2HTMLPaginationSelector'><option value='0'>----</option>";
|
||||
const values=converter.Pagination.options.values;
|
||||
let selectorsHTML="<label for='freeDatas2HTMLPaginationSelector'>"+converter.Pagination.options.name+" </label><select name='freeDatas2HTMLPaginationSelector' id='freeDatas2HTMLPaginationSelector'><option value='0'>----</option>";
|
||||
for(let j in values)
|
||||
selectorsHTML+="<option value='"+(Number(j)+1)+"'>"+values[j]+"</option>";
|
||||
selectorsHTML+="</select>";
|
||||
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="<table><thead>";
|
||||
@ -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+="<tr>";
|
||||
@ -465,33 +383,33 @@ export class freeDatas2HTML
|
||||
}
|
||||
datasHTML+="</tbody></table>";
|
||||
|
||||
// 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="<label for='freeDatas2HTMLPagesSelector'>"+this.pagination.pages.name+" </label><select name='freeDatas2HTMLPagesSelector' id='freeDatas2HTMLPagesSelector'><option value='1'>1</option>";
|
||||
this.pagination.pages.values=[1];
|
||||
let nbPages=Math.ceil(nbTotal/this.Pagination.selectedValue);
|
||||
let selectorsHTML="<label for='freeDatas2HTMLPagesSelector'>"+this.Pagination.pages.name+" </label><select name='freeDatas2HTMLPagesSelector' id='freeDatas2HTMLPagesSelector'><option value='1'>1</option>";
|
||||
this.Pagination.pages.values=[1];
|
||||
for(let j=2; j <= nbPages; j++)
|
||||
{
|
||||
selectorsHTML+="<option value='"+j+"'>"+j+"</option>";
|
||||
this.pagination.pages.values.push(j);
|
||||
this.Pagination.pages.values.push(j);
|
||||
}
|
||||
selectorsHTML+="</select>";
|
||||
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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
161
src/freeDatas2HTMLSelector.ts
Normal file
161
src/freeDatas2HTMLSelector.ts
Normal file
@ -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 <select> dans le HTML correspondant au filtre
|
||||
public selector2HTML() : void
|
||||
{
|
||||
if(this._converter === undefined || this._datasViewElt.eltDOM === undefined || this._datasFieldNb === undefined)
|
||||
throw new Error(errors.selector2HTMLFail);
|
||||
else
|
||||
{
|
||||
this.name=this._converter.parseMetas!.fields![this._datasFieldNb]; // this.converter.parse... ne peut être indéfinis si this.converter existe (cf constructeur)
|
||||
for (let row in this._converter.parseDatas)
|
||||
{
|
||||
if(this._separator === undefined)
|
||||
{
|
||||
let checkedValue=this._converter.parseDatas[row][this.name].trim();
|
||||
if(checkedValue !== "" && this.values.indexOf(checkedValue) === -1)
|
||||
this.values.push(checkedValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
let checkedValues=String(this._converter.parseDatas[row][this.name]).split(this._separator); // les données peuvent être des chiffres, etc.
|
||||
for(let i in checkedValues)
|
||||
{
|
||||
let checkedValue=checkedValues[i].trim();
|
||||
if(checkedValue !== "" && this.values.indexOf(checkedValue) === -1)
|
||||
this.values.push(checkedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(this.values.length > 0)
|
||||
{
|
||||
if(this._converter.getSortingFunctionForField(this._datasFieldNb) !== undefined)
|
||||
this.values.sort(this._converter.getSortingFunctionForField(this._datasFieldNb)!.sort); // si une fonction est définie pour ce champ, sort() doit exister, cf. interface
|
||||
else
|
||||
this.values.sort(compare());
|
||||
let selectorsHTML="<label for='freeDatas2HTML_"+this._datasViewElt.id+"'>"+this.name+" : </label><select name='freeDatas2HTML_"+this._datasViewElt.id+"' id='freeDatas2HTML_"+this._datasViewElt.id+"'><option value='0'>----</option>"; // l'option zéro permet de rafraichir en ignorant ce filtre
|
||||
for(let j in this.values)
|
||||
selectorsHTML+="<option value='"+(Number(j)+1)+"'>"+this.values[j]+"</option>";
|
||||
selectorsHTML+="</select>";
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -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 <select> listant les valeurs distinctes du fichier, classées dans le bon ordre.", async () =>
|
||||
{
|
||||
converter.datasSelectors=[{ datasFieldNb:3, id:"selector1"},{ datasFieldNb:4, id:"selector2"}];
|
||||
await converter.run();
|
||||
expect(document.getElementById("selector1").innerHTML).toEqual(fixtures.selector1HTML);
|
||||
expect(document.getElementById("selector2").innerHTML).toEqual(fixtures.selector2HTML);
|
||||
});
|
||||
|
||||
it("Si des valeurs vides sont présentes dans une colonne utilisée pour un sélecteur, elles doivent être ignorées.", async () =>
|
||||
{
|
||||
converter.datasSourceUrl="http://localhost:9876/datas/datas1-emtyinfield.csv";
|
||||
converter.datasSelectors=[{ datasFieldNb:3, id:"selector1"}];
|
||||
await converter.run();
|
||||
expect(document.getElementById("selector1").innerHTML).toEqual(fixtures.selector1HTML);
|
||||
});
|
||||
|
||||
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.", async () =>
|
||||
{
|
||||
converter.datasSelectors=[{ datasFieldNb:3, id:"selector1"},{ datasFieldNb:4, id:"selector2"}];
|
||||
await converter.run();
|
||||
let selectElement = document.getElementById("freeDatas2HTMLSelector0") 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.", async () =>
|
||||
{
|
||||
converter.datasSelectors=[{ datasFieldNb:3, id:"selector1"},{ datasFieldNb:4, id:"selector2"}];
|
||||
await converter.run();
|
||||
let selectElement = document.getElementById("freeDatas2HTMLSelector0") as HTMLInputElement;
|
||||
selectElement.value="2";
|
||||
selectElement = document.getElementById("freeDatas2HTMLSelector1") 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);
|
||||
});
|
||||
|
||||
// Cas particulier des champs pouvant contenir plusieurs valeurs :
|
||||
it("Si un séparateur est fourni pour un sélecteur, les valeurs distinctes extraites de cette colonne doivent le prendre en compte.", async () =>
|
||||
{
|
||||
converter.datasSourceUrl="http://localhost:9876/datas/datas1+tagsfield.csv";
|
||||
converter.datasSelectors=[{ datasFieldNb:5, id:"selector1", separator:"|"}];
|
||||
await converter.run();
|
||||
expect(document.getElementById("selector1").innerHTML).toEqual(fixtures.selector1HTMLWithTags);
|
||||
});
|
||||
|
||||
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";
|
||||
converter.datasSelectors=[{ datasFieldNb:5, id:"selector1", separator:"|"}];
|
||||
await converter.run();
|
||||
let selectElement=document.getElementById("freeDatas2HTMLSelector0") 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";
|
||||
converter.datasSelectors=[{ datasFieldNb:4, id:"selector1"}, { datasFieldNb:5, id:"selector2", separator:"|"}];
|
||||
await converter.run();
|
||||
|
||||
let selectElement=document.getElementById("freeDatas2HTMLSelector1") 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("freeDatas2HTMLSelector0") as HTMLInputElement;
|
||||
selectElement.value="3"; // doit supprimer la ligne restant
|
||||
selectElement.dispatchEvent(new Event('change'));
|
||||
txtDatasViewsElt=document.getElementById("datas").innerHTML;
|
||||
expect(txtDatasViewsElt).toEqual("<table><thead><tr><th>Z (numéro atomique)</th><th>Élément</th><th>Symbole</th><th>Famille</th><th>Abondance des éléments dans la croûte terrestre (μg/k)</th><th>Étiquettes</th></tr></thead><tbody></tbody></table>");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Création et action des colonnes permettant de classer les données affichées.", () =>
|
||||
{
|
||||
beforeEach( () =>
|
||||
@ -348,7 +219,7 @@ describe("freeDatas2HTML", () =>
|
||||
converter.datasSourceUrl="http://localhost:9876/datas/datas1.csv";
|
||||
});
|
||||
|
||||
it("Doit générer une erreur si au moins un des numéros de colonne de classement fournis pour ne correspond pas à une des colonne du fichier.", async () =>
|
||||
it("Doit générer une erreur si au moins un des numéros de colonne de classement fournis pour le classement ne correspond pas à une des colonne du fichier.", async () =>
|
||||
{
|
||||
converter.datasSortingColumns=[{ datasFieldNb:0 },{ datasFieldNb:5 }];
|
||||
await expectAsync(converter.run()).toBeRejectedWith(new Error(errors.sortingColumnsFieldNotFound));
|
||||
@ -368,7 +239,8 @@ describe("freeDatas2HTML", () =>
|
||||
expect(getTableTr[0].innerHTML).toEqual(fixtures.sortingColumn1HTML);
|
||||
expect(getTableTr[2].innerHTML).toEqual(fixtures.sortingColumn2HTML);
|
||||
});
|
||||
|
||||
|
||||
/* Désactivation des tests liés aux filtres avant mises à jour
|
||||
it("Le 1er click sur l'entête d'une des colonnes doit classer les données dans le sens ascendant, puis descendant et ainsi de suite, en prenant en compte les éventuels filtres.", async () =>
|
||||
{
|
||||
converter.datasSelectors=[{ datasFieldNb:3, id:"selector1"},{ datasFieldNb:4, id:"selector2"}];
|
||||
@ -389,7 +261,7 @@ describe("freeDatas2HTML", () =>
|
||||
getTableTrLink.click();// de nouveau ascendant
|
||||
txtDatasViewsElt=document.getElementById("datas").innerHTML;
|
||||
expect(txtDatasViewsElt).toEqual(fixtures.datasHTMLFor2Select1Clic);
|
||||
});
|
||||
});*/
|
||||
});
|
||||
|
||||
describe("Création et action des options permettant de paginer les données affichées.", () =>
|
||||
@ -398,7 +270,7 @@ describe("freeDatas2HTML", () =>
|
||||
{
|
||||
converter.datasViewElt={ id:"datas" };
|
||||
converter.datasSourceUrl="http://localhost:9876/datas/datas1.csv";
|
||||
converter.pagination=
|
||||
converter.Pagination=
|
||||
{
|
||||
selectedValue:10,
|
||||
options:
|
||||
@ -425,7 +297,7 @@ describe("freeDatas2HTML", () =>
|
||||
{
|
||||
await converter.run();
|
||||
let getTableTr=document.querySelectorAll("tr");// attention, un tr sert aux titres
|
||||
expect(getTableTr.length).toEqual(converter.pagination.selectedValue+1);
|
||||
expect(getTableTr.length).toEqual(converter.Pagination.selectedValue+1);
|
||||
});
|
||||
|
||||
it("Si une des options de pagination fournies est sélectionnée, doit afficher la première page de résultats correspondants.", async () =>
|
||||
@ -440,7 +312,7 @@ describe("freeDatas2HTML", () =>
|
||||
selectElement.dispatchEvent(new Event('change'));
|
||||
getTableTr=document.querySelectorAll("tr");
|
||||
expect(getTableTr.length).toEqual(51);
|
||||
selectElement.value="0"; // = pas de pagination, on affiche les 118 lignes du fichier
|
||||
selectElement.value="0"; // = pas de Pagination, on affiche les 118 lignes du fichier
|
||||
selectElement.dispatchEvent(new Event('change'));
|
||||
getTableTr=document.querySelectorAll("tr");
|
||||
expect(getTableTr.length).toEqual(119);
|
||||
@ -452,7 +324,7 @@ describe("freeDatas2HTML", () =>
|
||||
let btnPaginationElt=document.getElementById("pages").innerHTML;
|
||||
expect(btnPaginationElt).toEqual(fixtures.selectorForPages);
|
||||
});
|
||||
|
||||
|
||||
it("Si l'utilisateur sélectionne une des pages proposées, l'affichage des résultats doit s'adapter en prenant en compte la pagination sélectionnée.", async () =>
|
||||
{
|
||||
await converter.run();
|
||||
|
293
tests/selectorsSpec.ts
Normal file
293
tests/selectorsSpec.ts
Normal file
@ -0,0 +1,293 @@
|
||||
import { FreeDatas2HTML, Selector } from "../src/freeDatas2HTML";
|
||||
|
||||
const errors=require("../src/errors.js");
|
||||
const fixtures=require("./fixtures.js");
|
||||
|
||||
describe("Test des filtres de données", () =>
|
||||
{
|
||||
let converter: FreeDatas2HTML;
|
||||
let selector: Selector;
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
afterEach( () =>
|
||||
{
|
||||
document.body.removeChild(document.getElementById("fixture"));
|
||||
});
|
||||
|
||||
describe("Test des données reçues pour configurer un filtre.", () =>
|
||||
{
|
||||
it("Doit générer une erreur, si initialisé sans fournir la liste des champs servant à classer les données.", () =>
|
||||
{
|
||||
converter=new FreeDatas2HTML();
|
||||
expect(() => { return new Selector(converter); }).toThrowError(errors.selectorNeedDatas);
|
||||
});
|
||||
|
||||
it("Ne doit pas générer d'erreur, si initialisé avec des données correctes.", () =>
|
||||
{
|
||||
expect(() => { return new Selector(converter); }).not.toThrowError();
|
||||
});
|
||||
|
||||
it("Doit générer une erreur s'il n'y a aucun élément trouvé dans le DOM pour l'id fourni.", () =>
|
||||
{
|
||||
expect(() => { return converter.datasViewElt={ id:"dontExist" }; }).toThrowError(errors.elementNotFound+"dontExist");
|
||||
});
|
||||
|
||||
it("Si un élément est trouvé dans le DOM pour l'id fourni, doit l'accepter.", () =>
|
||||
{
|
||||
selector.datasViewElt={ id:"selector1"};
|
||||
let myContainer=document.getElementById("selector1");
|
||||
expect(selector.datasViewElt).toEqual({ id:"selector1", "eltDOM": myContainer });
|
||||
});
|
||||
|
||||
it("Doit générer une erreur, si le numéro de champ fourni n'existe pas dans les données fournies.", () =>
|
||||
{
|
||||
expect(() => { return selector.datasFieldNb=9; }).toThrowError(errors.selectorFieldNotFound);
|
||||
expect(() => { return selector.datasFieldNb=-1; }).toThrowError(errors.selectorFieldNotFound);
|
||||
expect(() => { return selector.datasFieldNb=1.1; }).toThrowError(errors.selectorFieldNotFound);
|
||||
});
|
||||
|
||||
it("Si le numéro de champ fourni est valide, il doit être accepté.", () =>
|
||||
{
|
||||
expect(() => { return selector.datasFieldNb=1; }).not.toThrowError();
|
||||
});
|
||||
|
||||
it("Si un séparateur vide est fourni pour un filtre, il doit être ignoré.", () =>
|
||||
{
|
||||
selector.separator="";
|
||||
expect(selector.separator).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Création et action des sélecteurs permettant de filter les données affichées.", () =>
|
||||
{
|
||||
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("Doit générer un élement <select> 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;
|
||||