Ajout fonctionnalité pagination des données.

This commit is contained in:
Fabrice PENHOËT 2021-09-09 12:57:23 +02:00
parent 66ea27b0fb
commit a4b7e7af4a
9 changed files with 397 additions and 43 deletions

View File

@ -19,13 +19,15 @@ De même l'idée est de rester libre du rendu des données en n'imposant pas de
La première version se contente de récupérer et parser des données présentes dans un fichier CSV via un appel Ajax. La première version se contente de récupérer et parser des données présentes dans un fichier CSV via un appel Ajax.
Les données trouvées sont affichées dans un tableau. En option, des colonnes peuvent être indiquées par filtrer les données et/ou les classer. Les données trouvées sont affichées dans un tableau. En option, des colonnes peuvent être indiquées par filtrer les données et/ou les classer.
Il est possible de fournir des fonctions spécifiques pour classer les données de certaines colonnes. Il est possible de fournir des fonctions spécifiques pour classer les données de certaines colonnes.
Il est également possible de paginer les résultats.
Le tout **en options**, le développeur final devant pouvoir adapter le module à son besoin.
Il reste à ajouter : Il reste à ajouter :
- la possibilité de paginer les données.
- la possibilité d'utiliser des sources/formats différents qu'un fichier CSV pour extraire les données. - la possibilité d'utiliser des sources/formats différents qu'un fichier CSV pour extraire les données.
- la possibilité de spécifier un code HTML autre qu'un tableau pour lister les données. - la possibilité de spécifier un code HTML autre qu'un tableau pour lister/récupérer les données.
Le tout en options, le développeur final devant pouvoir adapter le module à son besoin. Mais avant toute chose, il me faut maintenant **remanier le code, le script approchant les 500 lignes** !
Bref, il reste beaucoup de choses à faire ! Bref, il reste beaucoup de choses à faire !

View File

@ -11,15 +11,23 @@
</head> </head>
<body class="paper container"> <body class="paper container">
<h1>freeDatas2HTML</h1> <h1>freeDatas2HTML</h1>
<p id="filtre1"></p>
<p id="filtre2"></p> <div class="row">
<p id="filtre3"></p> <div id="filtre1" class="sm-12 md-6 lg-4 col"></div>
<div id="filtre2" class="sm-12 md-6 lg-4 col"></div>
<div id="filtre3" class="sm-12 md-6 lg-4 col"></div>
<div id="paginationOptions" class="col-12 col"></p>
</div>
<article id="datas"> <article id="datas">
<p>Si tout se passe bien, ce texte sera remplacé par un tableau de données extraites du fichier csv.</p> <p>Si tout se passe bien, ce texte sera remplacé par un tableau de données extraites du fichier csv.</p>
</article> </article>
<p id="pages"></p>
<aside><p>Les données affichées viennent d'<a href="datas/elements-chimiques.csv">un fichier CSV</a> reprenant une partie des informations trouvées <a href="https://fr.wikipedia.org/wiki/%C3%89l%C3%A9ment_chimique#Caract%C3%A9ristiques_des_diff%C3%A9rents_%C3%A9l%C3%A9ments" target="_blank">sur Wikipédia</a>.</p> <aside><p>Les données affichées viennent d'<a href="datas/elements-chimiques.csv">un fichier CSV</a> reprenant une partie des informations trouvées <a href="https://fr.wikipedia.org/wiki/%C3%89l%C3%A9ment_chimique#Caract%C3%A9ristiques_des_diff%C3%A9rents_%C3%A9l%C3%A9ments" target="_blank">sur Wikipédia</a>.</p>
<p>La dernière colonne permet de montrer la possibilité d'<b>extraire des données d'une colonne ayant plusieurs valeurs par ligne</b>, ici séparées par une virgule.<br>Ce choix de la virgule comme séparateur ne pose pas de problème, tant le fichier CSV est correctement conçu.<br>Elle ne doit par contre pas être présente dans les expressions à extraire de la colonne, sachant que l'on peut désigner n'importe quel autre caractère séparateur.</p> <p>La dernière colonne permet de montrer la possibilité d'<b>extraire des données d'une colonne ayant plusieurs valeurs par ligne</b>, ici séparées par une virgule.<br>Ce choix de la virgule comme séparateur ne pose pas de problème, tant le fichier CSV est correctement conçu.<br>Elle ne doit par contre pas être présente dans les expressions à extraire de la colonne, sachant que l'on peut désigner n'importe quel autre caractère séparateur.</p>
<p>Toujours en option, certaines peuvent être désignées comme permettant de classer les données: un 1ᵉʳ clic lance un classement par ordre croissant, le 2ᵉ pour ordre décroissant et ainsi de suite.</p> <p>Toujours en option, certaines peuvent être désignées comme permettant de classer les données: un 1ᵉʳ clic lance un classement par ordre croissant, le 2ᵉ pour ordre décroissant et ainsi de suite.</p>
<p>Enfin, une dernière option permet de <b>paginer les résultat</b>.</p>
<p>Design basé sur <a href="https://www.getpapercss.com" target="_blank">PaperCSS</a></p></aside> <p>Design basé sur <a href="https://www.getpapercss.com" target="_blank">PaperCSS</a></p></aside>
</body> </body>
</html> </html>

View File

@ -4,6 +4,9 @@ module.exports =
elementNotFound : "Aucun élément HTML n'a été trouvé ayant comme \"id\" : ", elementNotFound : "Aucun élément HTML n'a été trouvé ayant comme \"id\" : ",
needDatasElt: "Merci de fournir un id valide pour l'élément où afficher les données.", needDatasElt: "Merci de fournir un id valide pour l'élément où afficher les données.",
needNaturalNumber: "Merci de fournir un nombre entier supérieur ou égal à zéro pour désigner chaque colonne.", needNaturalNumber: "Merci de fournir un nombre entier supérieur ou égal à zéro pour désigner chaque colonne.",
needPagesSelectorElt: "Merci de fournir l'id de l'élément où afficher le sélecteur de pages.",
needPaginationByDefaultBeInOptions: "La valeur de pagination par défaut doit faire partie des options proposées.",
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.", needUrl: "Merci de fournir une url valide pour le fichier CSV à parser.",
parserFail: "La lecture des données du fichier a échoué.", 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.", selectorFieldNotFound: "Au moins une des colonnes devant servir à filtrer les données n'existe pas dans le fichier.",

View File

@ -7,6 +7,7 @@ const initialise = async () =>
let converter=new freeDatas2HTML(); let converter=new freeDatas2HTML();
converter.datasViewElt={ id:"datas" }; converter.datasViewElt={ id:"datas" };
converter.datasSelectors=[{ datasFieldNb:3, id:"filtre1"}, { datasFieldNb:4, id:"filtre2"},{ datasFieldNb:5, id:"filtre3", separator:"," }]; converter.datasSelectors=[{ datasFieldNb:3, id:"filtre1"}, { datasFieldNb:4, id:"filtre2"},{ datasFieldNb:5, id:"filtre3", separator:"," }];
const mySort = (a: any, b: any, order: "asc"|"desc" = "asc") => const mySort = (a: any, b: any, order: "asc"|"desc" = "asc") =>
{ {
const values = [ "> 100000", "> 1 et < 100 000", "≤ 1", "Traces", "Inexistant"]; const values = [ "> 100000", "> 1 et < 100 000", "≤ 1", "Traces", "Inexistant"];
@ -21,6 +22,21 @@ const initialise = async () =>
}; };
converter.datasSortingColumns=[{ datasFieldNb:0 }, { datasFieldNb:1 },{ datasFieldNb:2 }, { datasFieldNb:4 }]; converter.datasSortingColumns=[{ datasFieldNb:0 }, { datasFieldNb:1 },{ datasFieldNb:2 }, { datasFieldNb:4 }];
converter.datasSortingFunctions= [{ datasFieldNb:4, sort:mySort}]; converter.datasSortingFunctions= [{ datasFieldNb:4, sort:mySort}];
converter.pagination=
{
selectedValue:10,
options:
{
displayElement : { id:"paginationOptions" },
values: [10,20,50,500],
name: "Choix de pagination :"
},
pages:
{
displayElement : { id:"pages" },
name: "Page à afficher :"
}
}
converter.datasSourceUrl="http://localhost:8080/datas/elements-chimiques.csv"; converter.datasSourceUrl="http://localhost:8080/datas/elements-chimiques.csv";
await converter.run(); await converter.run();
} }

View File

@ -3,7 +3,7 @@ const errors = require("./errors.js");
const { compare }= require('natural-orderby'); const { compare }= require('natural-orderby');
import { papaParseDatas, papaParseErrors, papaParseMeta } from "./papaParseInterfaces"; import { papaParseDatas, papaParseErrors, papaParseMeta } from "./papaParseInterfaces";
import { domElement, selectors, sortingColumns, sortingFunctions } from "./freeDatas2HTMLInterfaces"; import { domElement, pagination, selectors, sortingColumns, sortingFunctions } from "./freeDatas2HTMLInterfaces";
export class freeDatas2HTML export class freeDatas2HTML
{ {
@ -20,6 +20,13 @@ export class freeDatas2HTML
public parseDatas: papaParseDatas[] = []; public parseDatas: papaParseDatas[] = [];
public parseErrors: papaParseErrors[] = []; public parseErrors: papaParseErrors[] = [];
public stopIfParseErrors: boolean = false; public stopIfParseErrors: boolean = false;
// Pagination :
private _pagination: pagination|undefined;
public static isPositiveInteger(nb: number)
{
return (Number.isInteger(nb) === false || nb <= 0) ? false : true;
}
public static isNaturalNumber(nb: number) public static isNaturalNumber(nb: number)
{ {
@ -50,7 +57,7 @@ export class freeDatas2HTML
{ {
this._datasSelectors=[]; this._datasSelectors=[];
let checkContainerExist: HTMLElement|null; let checkContainerExist: HTMLElement|null;
for(let i = 0; i < selectionElts.length; i++) for(let i in selectionElts)
{ {
checkContainerExist=document.getElementById(selectionElts[i].id); checkContainerExist=document.getElementById(selectionElts[i].id);
if(checkContainerExist === null) if(checkContainerExist === null)
@ -116,6 +123,69 @@ export class freeDatas2HTML
return undefined; return undefined;
} }
// Long et tortueux ! créer une classe dédiée ?
set pagination(config: 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)
{
if(config.pages === undefined)
throw new Error(errors.needPagesSelectorElt);
let checkContainerExist=document.getElementById(config.pages.displayElement.id);
if(checkContainerExist === null)
throw new Error(errors.elementNotFound+config.pages.displayElement.id);
else
{
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
}
}
}
if(config.options !== undefined)
{
let checkContainerExist=document.getElementById(config.options.displayElement.id);
if(checkContainerExist === null)
throw new Error(errors.elementNotFound+config.options.displayElement.id);
else
{
for(let i = 0; i < config.options.values.length; i++)
{
if(freeDatas2HTML.isPositiveInteger(config.options.values[i]) === false)
throw new Error(errors.needPositiveInteger);
}
this._pagination.options =
{
displayElement: { id:config.options.displayElement.id, eltDOM:checkContainerExist },
name: (config.options.name) ? config.options.name : "Pagination :",
values:config.options.values
};
}
}
// Valeur de pagination par défaut qui peut être différente de celles éventuellement proposées en option :
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;
else
throw new Error(errors.needPositiveInteger);
}
}
get pagination(): pagination
{
return <pagination>this._pagination;
}
public async parse(): Promise<any> public async parse(): Promise<any>
{ {
const converter=this; const converter=this;
@ -228,6 +298,34 @@ export class freeDatas2HTML
} }
} }
// 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>";
for(let j in values)
selectorsHTML+="<option value='"+(Number(j)+1)+"'>"+values[j]+"</option>";
selectorsHTML+="</select>";
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)
{
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;
else
converter.pagination.selectedValue=values[Number(selectElement.value)-1];
converter.datasHTML=converter.createDatasHTML(converter.parseMeta!.fields as string[], converter.parseDatas);
converter.refreshView();
});
}
// Je teste aussi les colonnes devant servir à classer les données. // Je teste aussi les colonnes devant servir à classer les données.
for(let i in this._datasSortingColumns) for(let i in this._datasSortingColumns)
{ {
@ -306,41 +404,50 @@ export class freeDatas2HTML
datas.sort( (a, b) => compare( {order: colOrder} )(a[col], b[col])); datas.sort( (a, b) => compare( {order: colOrder} )(a[col], b[col]));
} }
// 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;
// Création du tableau de données : // Création du tableau de données :
let datasHTML="<table><thead>"; let datasHTML="<table><thead>";
for (let i in fields) for (let i in fields)
datasHTML+="<th>"+fields[i]+"</th>"; datasHTML+="<th>"+fields[i]+"</th>";
datasHTML+="</thead><tbody>"; datasHTML+="</thead><tbody>";
let nbVisible=0, nbTotal=0;
for (let row in datas) for (let row in datas)
{ {
let visible=true; let visible=true;
if(filters.length !== 0) if(filters.length !== 0)
{ {
for(let i in filters) let i=0;
while(filters[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 // 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(filters[i].separator === undefined)
{ {
if(datas[row][filters[i].field].trim() != filters[i].value) if(datas[row][filters[i].field].trim() !== filters[i].value)
visible=false; visible=false;
} }
else else
{ {
let checkedValues=datas[row][filters[i].field].split(filters[i].separator as string), finded=false; visible=false;
let checkedValues=datas[row][filters[i].field].split(filters[i].separator as string);
for(let j in checkedValues) for(let j in checkedValues)
{ {
if(checkedValues[j].trim() === filters[i].value) if(checkedValues[j].trim() === filters[i].value)
{ {
finded=true; visible=true;
break; break;
} }
} }
if(!finded) }
visible=false; i++;
} }
} }
}
if(visible) if(visible && nbTotal >= firstData && nbVisible < maxData)
{ {
datasHTML+="<tr>"; datasHTML+="<tr>";
for(let field in datas[row]) for(let field in datas[row])
@ -350,9 +457,42 @@ export class freeDatas2HTML
datasHTML+="<td>"+datas[row][field]+"</td>"; datasHTML+="<td>"+datas[row][field]+"</td>";
} }
datasHTML+="</tr>"; datasHTML+="</tr>";
nbVisible++;
nbTotal++;
} }
else if(visible)
nbTotal++;
} }
datasHTML+="</tbody></table>"; 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)
{
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);
}
selectorsHTML+="</select>";
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;
let converter=this;
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.refreshView();
});
}
else if(this.pagination !== undefined && this.pagination.pages !== undefined)
this.pagination.pages.displayElement.eltDOM!.innerHTML="";
return datasHTML; return datasHTML;
} }
} }

View File

@ -3,7 +3,7 @@ export interface domElement
id: string; id: string;
eltDOM?: HTMLElement; eltDOM?: HTMLElement;
} }
export interface selectors extends domElement export interface selectors extends domElement // revoir pour donner un autre nom
{ {
datasFieldNb: number; datasFieldNb: number;
separator?: string; separator?: string;
@ -20,3 +20,20 @@ export interface sortingFunctions
datasFieldNb: number; 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 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
{
options?:
{
displayElement: domElement;
name?: string; // rendre obligatoire ?
values: number[];
};
selectedValue?: number; // on peut utiliser une pagination sans proposer d'options à l'utilisateur.
pages?:
{
displayElement: domElement;
name?: string; // rendre obligatoire ?
values?: number[];
selectedValue?: number;
}
}

File diff suppressed because one or more lines are too long

View File

@ -34,11 +34,9 @@ describe("freeDatas2HTML", () =>
expect(() => { return converter.datasViewElt={ id:"datas" }; }).not.toThrowError(); expect(() => { return converter.datasViewElt={ id:"datas" }; }).not.toThrowError();
}); });
it("Ne doit accepter que les sélecteurs pour lesquels un élément a été trouvé dans la page pour l'id fourni.", () => it("Doit générer une erreur si l'url fournie pour le fichier de données est vide.", () =>
{ {
converter.datasSelectors=[{ datasFieldNb:2, id:"selector2" },{ datasFieldNb:3, id:"selector3" }]; expect(() => { return converter.datasSourceUrl=" "; }).toThrowError(errors.needUrl);
expect(converter.datasSelectors.length).toEqual(1);
expect(converter.datasSelectors[0].id).toEqual("selector2");
}); });
it("Doit retourner un booléen indiquant si un nombre est naturel ou non.", () => it("Doit retourner un booléen indiquant si un nombre est naturel ou non.", () =>
@ -49,6 +47,22 @@ describe("freeDatas2HTML", () =>
expect(freeDatas2HTML.isNaturalNumber(1)).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é.", () => it("Si un séparateur vide est fourni pour un sélecteur, il doit être ignoré.", () =>
{ {
converter.datasSelectors=[{ datasFieldNb:2, id:"selector2", separator:"" }]; converter.datasSelectors=[{ datasFieldNb:2, id:"selector2", separator:"" }];
@ -61,11 +75,7 @@ describe("freeDatas2HTML", () =>
expect(converter.datasSelectors.length).toEqual(2); expect(converter.datasSelectors.length).toEqual(2);
}); });
it("Doit générer une erreur si l'url fournie pour le fichier de données est vide.", () => // Classement des données :
{
expect(() => { return converter.datasSourceUrl=" "; }).toThrowError(errors.needUrl);
});
it("Doit me retourner la fonction associée à une colonne, de manière à ce qu'elle soit utilisable pour comparer deux valeurs.", () => it("Doit me retourner la fonction associée à une colonne, de manière à ce qu'elle soit utilisable pour comparer deux valeurs.", () =>
{ {
// Fonction volontairement basique, car ce n'est pas la fonction que l'on teste ici, mais le fait que l'on puisse l'utiliser ! // Fonction volontairement basique, car ce n'est pas la fonction que l'on teste ici, mais le fait que l'on puisse l'utiliser !
@ -82,6 +92,58 @@ describe("freeDatas2HTML", () =>
expect(converter.getSortingFunctionForField(0)).toBeDefined(); expect(converter.getSortingFunctionForField(0)).toBeDefined();
expect([7,9,3,5].sort(converter.getSortingFunctionForField(0).sort)).toEqual([9,7,5,3]); expect([7,9,3,5].sort(converter.getSortingFunctionForField(0).sort)).toEqual([9,7,5,3]);
}); });
// 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);
});
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");
});
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");
});
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);
});
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);
});
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);
});
it("Doit accepter une configuration correcte pour la pagination.", () =>
{
let paginationOk =
{
selectedValue:10,
options:
{
displayElement : { id:"paginationOptions" },
values: [10,20,50],
name: "Choix de pagination :"
},
pages:
{
displayElement : { id:"pages" },
name: "Page à afficher :"
}
};
expect(() => { return converter.pagination=paginationOk; }).not.toThrowError();
});
}); });
describe("Parsage du fichier et création du tableau de données", () => describe("Parsage du fichier et création du tableau de données", () =>
@ -257,6 +319,25 @@ describe("freeDatas2HTML", () =>
let txtDatasViewsElt=document.getElementById("datas").innerHTML; let txtDatasViewsElt=document.getElementById("datas").innerHTML;
expect(txtDatasViewsElt).toEqual(fixtures.datasHTMLForSelectTagsField); 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.", () => describe("Création et action des colonnes permettant de classer les données affichées.", () =>
@ -283,9 +364,9 @@ describe("freeDatas2HTML", () =>
{ {
converter.datasSortingColumns=[{ datasFieldNb:0 },{ datasFieldNb:2 }]; converter.datasSortingColumns=[{ datasFieldNb:0 },{ datasFieldNb:2 }];
await converter.run(); await converter.run();
let getTableTh=document.querySelectorAll("table th"); let getTableTr=document.querySelectorAll("table th");
expect(getTableTh[0].innerHTML).toEqual(fixtures.sortingColumn1HTML); expect(getTableTr[0].innerHTML).toEqual(fixtures.sortingColumn1HTML);
expect(getTableTh[2].innerHTML).toEqual(fixtures.sortingColumn2HTML); expect(getTableTr[2].innerHTML).toEqual(fixtures.sortingColumn2HTML);
}); });
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 () => 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 () =>
@ -298,16 +379,97 @@ describe("freeDatas2HTML", () =>
selectElement = document.getElementById("freeDatas2HTMLSelector1") as HTMLInputElement; selectElement = document.getElementById("freeDatas2HTMLSelector1") as HTMLInputElement;
selectElement.value="1"; selectElement.value="1";
selectElement.dispatchEvent(new Event('change')); selectElement.dispatchEvent(new Event('change'));
let getTableThLink=document.querySelector("table th a") as HTMLElement; let getTableTrLink=document.querySelector("table th a") as HTMLElement;
getTableThLink.click();// tri ascendant getTableTrLink.click();// tri ascendant
let txtDatasViewsElt=document.getElementById("datas").innerHTML; let txtDatasViewsElt=document.getElementById("datas").innerHTML;
expect(txtDatasViewsElt).toEqual(fixtures.datasHTMLFor2Select1Clic); expect(txtDatasViewsElt).toEqual(fixtures.datasHTMLFor2Select1Clic);
getTableThLink.click();// tri descendant getTableTrLink.click();// tri descendant
txtDatasViewsElt=document.getElementById("datas").innerHTML; txtDatasViewsElt=document.getElementById("datas").innerHTML;
expect(txtDatasViewsElt).toEqual(fixtures.datasHTMLFor2Select2Clic); expect(txtDatasViewsElt).toEqual(fixtures.datasHTMLFor2Select2Clic);
getTableThLink.click();// de nouveau ascendant getTableTrLink.click();// de nouveau ascendant
txtDatasViewsElt=document.getElementById("datas").innerHTML; txtDatasViewsElt=document.getElementById("datas").innerHTML;
expect(txtDatasViewsElt).toEqual(fixtures.datasHTMLFor2Select1Clic); expect(txtDatasViewsElt).toEqual(fixtures.datasHTMLFor2Select1Clic);
}); });
}); });
describe("Création et action des options permettant de paginer les données affichées.", () =>
{
beforeEach( () =>
{
converter.datasViewElt={ id:"datas" };
converter.datasSourceUrl="http://localhost:9876/datas/datas1.csv";
converter.pagination=
{
selectedValue:10,
options:
{
displayElement : { id:"paginationOptions" },
values: [10,20,50,500],
name: "Choix de pagination :"
},
pages:
{
displayElement : { id:"pages" },
name: "Page à afficher :"
}
}
});
it("Si des options de pagination sont fournies, doit générer un élement <select> listant les valeurs possibles.", async () =>
{
await converter.run();
expect(document.getElementById("paginationOptions").innerHTML).toEqual(fixtures.selectorForPagination);
});
it("Si une valeur de pagination par défaut fournie, ne doit pas afficher plus de données.", async () =>
{
await converter.run();
let getTableTr=document.querySelectorAll("tr");// attention, un tr sert aux titres
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 () =>
{
await converter.run();
let selectElement = document.getElementById("freeDatas2HTMLPaginationSelector") as HTMLInputElement;
selectElement.value="2"; // = 20 éléments / page
selectElement.dispatchEvent(new Event('change'));
let getTableTr=document.querySelectorAll("tr");
expect(getTableTr.length).toEqual(21);
selectElement.value="3"; // = 50 éléments / page
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.dispatchEvent(new Event('change'));
getTableTr=document.querySelectorAll("tr");
expect(getTableTr.length).toEqual(119);
});
it("Si il y a plus de données que le nombre de lignes autorisées par page, un <select> listant les pages doit être affichés.", async () =>
{
await converter.run();
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();
let selectElement = document.getElementById("freeDatas2HTMLPaginationSelector") as HTMLInputElement;
selectElement.value="3"; // = 50 éléments / page
selectElement.dispatchEvent(new Event('change'));
selectElement=document.getElementById("freeDatas2HTMLPagesSelector") as HTMLInputElement;
selectElement.value="2";
selectElement.dispatchEvent(new Event('change'));
let getTableTr=document.getElementsByTagName("tr");
expect(getTableTr[1].innerHTML).toEqual(fixtures.firstLineForPageSelection1);
expect(getTableTr[50].innerHTML).toEqual(fixtures.lastLineForPageSelection1);
selectElement.value="3"; // troisième page = incomplèet (18 enregistrements)
selectElement.dispatchEvent(new Event('change'));
getTableTr=document.getElementsByTagName("tr");
expect(getTableTr[1].innerHTML).toEqual(fixtures.firstLineForPageSelection2);
expect(getTableTr[18].innerHTML).toEqual(fixtures.lastLineForPageSelection2);
});
});
}); });