Nouvelle version avec possibilité de liste de filtre permettant de sélectionner plusieurs valeurs.

This commit is contained in:
Fabrice PENHOËT 2022-01-13 17:25:06 +01:00
parent 6532c04ca1
commit 7a4992a243
9 changed files with 321 additions and 201 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "freedatas2html", "name": "freedatas2html",
"version": "1.1.0", "version": "1.2.0",
"description": "Conversion and display of data in different formats (CSV, JSON, HTML) with the possibility of filtering, classifying and paginating the results.", "description": "Conversion and display of data in different formats (CSV, JSON, HTML) with the possibility of filtering, classifying and paginating the results.",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@ -8,10 +8,11 @@ export class Selector implements Selectors
private _converter: FreeDatas2HTML; private _converter: FreeDatas2HTML;
private _datasFieldNb: number; private _datasFieldNb: number;
private _datasViewElt: DOMElement={ id: "", eltDOM: undefined }; private _datasViewElt: DOMElement={ id: "", eltDOM: undefined };
private _selectedValue: number|undefined=undefined; private _selectedValues: number[]=[];
private _separator: string|undefined; private _separator: string|undefined;
private _values: string[]=[]; private _values: string[]=[];
private _name: string=""; private _name: string="";
public isMultiple: boolean=false;// permet à l'utilisateur de sélectionner plusieurs valeurs dans la liste.
// Injection de la classe principale, mais uniquement si des données ont été importées. // Injection de la classe principale, mais uniquement si des données ont été importées.
// Le champ duquel le sélecteur tire ses données doit exister. // Le champ duquel le sélecteur tire ses données doit exister.
@ -29,6 +30,8 @@ export class Selector implements Selectors
// Pas de trim(), car l'espace peut être le séparateur : // Pas de trim(), car l'espace peut être le séparateur :
if(separator !== undefined && separator !== "") if(separator !== undefined && separator !== "")
this._separator=separator; this._separator=separator;
this._name=this._converter.fields[this._datasFieldNb];
this.setValues();
} }
} }
@ -52,9 +55,9 @@ export class Selector implements Selectors
return this._name; return this._name;
} }
get selectedValue() : number|undefined get selectedValues() : number[]
{ {
return this._selectedValue; return this._selectedValues;
} }
get separator() : string|undefined get separator() : string|undefined
@ -67,11 +70,10 @@ export class Selector implements Selectors
return this._values; return this._values;
} }
// Création du <select> dans le DOM correspondant au filtre // Dédoublonnage et classement des valeurs disponibles pour le champ du sélecteur
public filter2HTML(label:string="") : void private setValues() :void
{ {
this._name=this._converter.fields![this._datasFieldNb]; // "!", car l'existence du champ est testé par le constructeur. for (const row of this._converter.datas)
for (let row of this._converter.datas)
{ {
let checkedValue; let checkedValue;
if(this._separator === undefined) if(this._separator === undefined)
@ -83,7 +85,7 @@ export class Selector implements Selectors
else else
{ {
let checkedValues=row[this._name].split(this._separator); let checkedValues=row[this._name].split(this._separator);
for(let value of checkedValues) for(const value of checkedValues)
{ {
checkedValue=value.trim(); checkedValue=value.trim();
if(checkedValue !== "" && this._values.indexOf(checkedValue) === -1) if(checkedValue !== "" && this._values.indexOf(checkedValue) === -1)
@ -91,7 +93,7 @@ export class Selector implements Selectors
} }
} }
} }
if(this._values.length === 0) // possible, si uniquement des valeurs vides pour ce champ. if(this._values.length === 0) // possible, si uniquement des valeurs vides pour ce champ.
throw new Error(errors.selectorFieldIsEmpty); throw new Error(errors.selectorFieldIsEmpty);
else else
@ -101,66 +103,96 @@ export class Selector implements Selectors
this._values.sort(this._converter.getSortingFunctionForField(this._datasFieldNb)!.sort); // sans le "!" : TS2532: Object is possibly 'undefined' ??? this._values.sort(this._converter.getSortingFunctionForField(this._datasFieldNb)!.sort); // sans le "!" : TS2532: Object is possibly 'undefined' ???
else else
this._values.sort(compare()); this._values.sort(compare());
// Création et injection du SELECT dans le DOM :
label=(label === "") ? this._name : label;
let selectorsHTML="<label for='freeDatas2HTML_"+this._datasViewElt.id+"'>"+label+" :</label><select name='freeDatas2HTML_"+this._datasViewElt.id+"' id='freeDatas2HTML_"+this._datasViewElt.id+"'><option value='0'>----</option>"; // l'option zéro permet d'actualiser l'affichage en ignorant ce filtre.
for(let i=0; i< this._values.length; i++)
selectorsHTML+="<option value='"+(i+1)+"'>"+this._values[i]+"</option>";
selectorsHTML+="</select>";
this. _datasViewElt.eltDOM!.innerHTML=selectorsHTML;// "!", car l'existence de "eltDOM" est testé par le constructeur.
// Actualisation de l'affichage lorsqu'une valeur est sélectionnée :
const selectElement=document.getElementById("freeDatas2HTML_"+this._datasViewElt.id) as HTMLInputElement, mySelector=this;
selectElement.addEventListener("change", function(e)
{
if(selectElement.value === "0")
mySelector._selectedValue=undefined;
else
mySelector._selectedValue=parseInt(selectElement.value,10)-1;
mySelector._converter.refreshView();
});
} }
} }
public dataIsOk(data: {[index: string]:string}) : boolean // Création du <select> dans le DOM correspondant au filtre
public filter2HTML(label:string="") : void
{ {
// Permet de vérifier que filter2HTML() a été préalablement appelée : label=(label === "") ? this._name : label;
if(this._name === "") const multipleAttr=(this.isMultiple)? " multiple" :"";
throw new Error(errors.filterCheckIsOkFail); let selectorsHTML="<label for='freeDatas2HTML_"+this._datasViewElt.id+"'>"+label+" :</label><select name='freeDatas2HTML_"+this._datasViewElt.id+"' id='freeDatas2HTML_"+this._datasViewElt.id+"'"+multipleAttr+"><option value='0'>----</option>"; // l'option zéro permet d'actualiser l'affichage en ignorant ce filtre.
for(let i=0; i< this._values.length; i++)
// Pas de valeur sélectionnée = pas de filtre sur ce champ selectorsHTML+="<option value='"+(i+1)+"'>"+this._values[i]+"</option>";
if(this._selectedValue === undefined) selectorsHTML+="</select>";
return true; this. _datasViewElt.eltDOM!.innerHTML=selectorsHTML;// "!", car l'existence de "eltDOM" est testé par le constructeur.
// Actualisation de l'affichage lorsqu'une valeur est sélectionnée :
if(this._values[this._selectedValue] === undefined) // théoriquement impossible, mais cela vient du client... const selectElement=document.getElementById("freeDatas2HTML_"+this._datasViewElt.id) as HTMLSelectElement, mySelector=this;
throw new Error(errors.selectorSelectedIndexNotFound); selectElement.addEventListener("change", function(e)
{
// Si le champ est absent pour un enregistrement, il est refusé mySelector._selectedValues=[];
if(data[this._name] === undefined) if(mySelector.isMultiple)
return false;
const selectedValueTxt=this._values[this._selectedValue];
if(this._separator === undefined)
{
if(data[this._name].trim() !== selectedValueTxt)
return false;
else
return true;
}
else
{
let find=false;
let checkedValues=data[this._name].split(this._separator);
for(let value of checkedValues)
{ {
if(value.trim() === selectedValueTxt) for(let i=0; i < selectElement.selectedOptions.length; i++)
{ {
find=true; const selectedValue=parseInt(selectElement.selectedOptions[i].value,10);
break; if(selectedValue === 0) // = annulation de ce filtre
{
mySelector._selectedValues=[];
break;
}
else
mySelector._selectedValues.push(selectedValue-1);
} }
} }
return find; else
{
let selectedValue=parseInt(selectElement.value,10);
if(selectedValue === 0) // = annulation de ce filtre
mySelector._selectedValues=[];
else
mySelector._selectedValues[0]=selectedValue-1;
}
mySelector._converter.refreshView();
});
}
public dataIsOk(data: {[index: string]:string}) : boolean
{
const checkIsValid=(selector : Selector, data: {[index: string]:string}, checkedValue:string) : boolean =>
{
if(selector._separator === undefined)
{
if(data[selector._name].trim() !== checkedValue)
return false;
else
return true;
}
else
{
let find=false;
let checkedValues=data[selector._name].split(selector._separator);
for(let value of checkedValues)
{
if(value.trim() === checkedValue)
{
find=true;
break;
}
}
return find;
}
};
// Pas de valeur sélectionnée = pas de filtre sur ce champ, donc tout passe :
if(this._selectedValues.length === 0)
return true;
// Un enregistrement n'ayant pas le champ du filtre sera refusé :
if(data[this._name] === undefined)
return false;
// Si plusieurs options sont sélectionnées dans une liste multiple,
// il suffit qu'une soit trouvée pour que l'enregistrement soit valide.
let find=false;
for(const value of this._selectedValues)
{
if(this._values[value] === undefined) // théoriquement impossible, mais cela vient du client...
throw new Error(errors.selectorSelectedIndexNotFound);
find=checkIsValid(this, data, this._values[value]);
if(find)
break;
} }
return find;
} }
} }

View File

@ -4,9 +4,10 @@ import { FreeDatas2HTML } from "./FreeDatas2HTML";
var Selector = (function () { var Selector = (function () {
function Selector(converter, datasFieldNb, elt, separator) { function Selector(converter, datasFieldNb, elt, separator) {
this._datasViewElt = { id: "", eltDOM: undefined }; this._datasViewElt = { id: "", eltDOM: undefined };
this._selectedValue = undefined; this._selectedValues = [];
this._values = []; this._values = [];
this._name = ""; this._name = "";
this.isMultiple = false;
if (converter.fields.length === 0 || converter.datas.length === 0) if (converter.fields.length === 0 || converter.datas.length === 0)
throw new Error(errors.filterNeedDatas); throw new Error(errors.filterNeedDatas);
else if (!converter.checkFieldExist(Number(datasFieldNb))) else if (!converter.checkFieldExist(Number(datasFieldNb)))
@ -17,6 +18,8 @@ var Selector = (function () {
this._datasFieldNb = datasFieldNb; this._datasFieldNb = datasFieldNb;
if (separator !== undefined && separator !== "") if (separator !== undefined && separator !== "")
this._separator = separator; this._separator = separator;
this._name = this._converter.fields[this._datasFieldNb];
this.setValues();
} }
} }
Object.defineProperty(Selector.prototype, "converter", { Object.defineProperty(Selector.prototype, "converter", {
@ -47,9 +50,9 @@ var Selector = (function () {
enumerable: true, enumerable: true,
configurable: true configurable: true
}); });
Object.defineProperty(Selector.prototype, "selectedValue", { Object.defineProperty(Selector.prototype, "selectedValues", {
get: function () { get: function () {
return this._selectedValue; return this._selectedValues;
}, },
enumerable: true, enumerable: true,
configurable: true configurable: true
@ -68,9 +71,7 @@ var Selector = (function () {
enumerable: true, enumerable: true,
configurable: true configurable: true
}); });
Selector.prototype.filter2HTML = function (label) { Selector.prototype.setValues = function () {
if (label === void 0) { label = ""; }
this._name = this._converter.fields[this._datasFieldNb];
for (var _i = 0, _a = this._converter.datas; _i < _a.length; _i++) { for (var _i = 0, _a = this._converter.datas; _i < _a.length; _i++) {
var row = _a[_i]; var row = _a[_i];
var checkedValue = void 0; var checkedValue = void 0;
@ -96,50 +97,76 @@ var Selector = (function () {
this._values.sort(this._converter.getSortingFunctionForField(this._datasFieldNb).sort); this._values.sort(this._converter.getSortingFunctionForField(this._datasFieldNb).sort);
else else
this._values.sort(compare()); this._values.sort(compare());
label = (label === "") ? this._name : label;
var selectorsHTML = "<label for='freeDatas2HTML_" + this._datasViewElt.id + "'>" + label + " :</label><select name='freeDatas2HTML_" + this._datasViewElt.id + "' id='freeDatas2HTML_" + this._datasViewElt.id + "'><option value='0'>----</option>";
for (var i = 0; i < this._values.length; i++)
selectorsHTML += "<option value='" + (i + 1) + "'>" + this._values[i] + "</option>";
selectorsHTML += "</select>";
this._datasViewElt.eltDOM.innerHTML = selectorsHTML;
var selectElement_1 = document.getElementById("freeDatas2HTML_" + this._datasViewElt.id), mySelector_1 = this;
selectElement_1.addEventListener("change", function (e) {
if (selectElement_1.value === "0")
mySelector_1._selectedValue = undefined;
else
mySelector_1._selectedValue = parseInt(selectElement_1.value, 10) - 1;
mySelector_1._converter.refreshView();
});
} }
}; };
Selector.prototype.dataIsOk = function (data) { Selector.prototype.filter2HTML = function (label) {
if (this._name === "") if (label === void 0) { label = ""; }
throw new Error(errors.filterCheckIsOkFail); label = (label === "") ? this._name : label;
if (this._selectedValue === undefined) var multipleAttr = (this.isMultiple) ? " multiple" : "";
return true; var selectorsHTML = "<label for='freeDatas2HTML_" + this._datasViewElt.id + "'>" + label + " :</label><select name='freeDatas2HTML_" + this._datasViewElt.id + "' id='freeDatas2HTML_" + this._datasViewElt.id + "'" + multipleAttr + "><option value='0'>----</option>";
if (this._values[this._selectedValue] === undefined) for (var i = 0; i < this._values.length; i++)
throw new Error(errors.selectorSelectedIndexNotFound); selectorsHTML += "<option value='" + (i + 1) + "'>" + this._values[i] + "</option>";
if (data[this._name] === undefined) selectorsHTML += "</select>";
return false; this._datasViewElt.eltDOM.innerHTML = selectorsHTML;
var selectedValueTxt = this._values[this._selectedValue]; var selectElement = document.getElementById("freeDatas2HTML_" + this._datasViewElt.id), mySelector = this;
if (this._separator === undefined) { selectElement.addEventListener("change", function (e) {
if (data[this._name].trim() !== selectedValueTxt) mySelector._selectedValues = [];
return false; if (mySelector.isMultiple) {
else for (var i = 0; i < selectElement.selectedOptions.length; i++) {
return true; var selectedValue = parseInt(selectElement.selectedOptions[i].value, 10);
} if (selectedValue === 0) {
else { mySelector._selectedValues = [];
var find = false; break;
var checkedValues = data[this._name].split(this._separator); }
for (var _i = 0, checkedValues_2 = checkedValues; _i < checkedValues_2.length; _i++) { else
var value = checkedValues_2[_i]; mySelector._selectedValues.push(selectedValue - 1);
if (value.trim() === selectedValueTxt) {
find = true;
break;
} }
} }
return find; else {
var selectedValue = parseInt(selectElement.value, 10);
if (selectedValue === 0)
mySelector._selectedValues = [];
else
mySelector._selectedValues[0] = selectedValue - 1;
}
mySelector._converter.refreshView();
});
};
Selector.prototype.dataIsOk = function (data) {
var checkIsValid = function (selector, data, checkedValue) {
if (selector._separator === undefined) {
if (data[selector._name].trim() !== checkedValue)
return false;
else
return true;
}
else {
var find_1 = false;
var checkedValues = data[selector._name].split(selector._separator);
for (var _i = 0, checkedValues_2 = checkedValues; _i < checkedValues_2.length; _i++) {
var value = checkedValues_2[_i];
if (value.trim() === checkedValue) {
find_1 = true;
break;
}
}
return find_1;
}
};
if (this._selectedValues.length === 0)
return true;
if (data[this._name] === undefined)
return false;
var find = false;
for (var _i = 0, _a = this._selectedValues; _i < _a.length; _i++) {
var value = _a[_i];
if (this._values[value] === undefined)
throw new Error(errors.selectorSelectedIndexNotFound);
find = checkIsValid(this, data, this._values[value]);
if (find)
break;
} }
return find;
}; };
return Selector; return Selector;
}()); }());

View File

@ -5,7 +5,6 @@ module.exports =
converterFieldNotFound: "Le champ n'existe pas dans les données ou les données n'ont pas encore été chargées.", converterFieldNotFound: "Le champ n'existe pas dans les données ou les données n'ont pas encore été chargées.",
converterNeedDatasElt: "Merci de fournir un id valide pour l'élément où afficher les données.", converterNeedDatasElt: "Merci de fournir un id valide pour l'élément où afficher les données.",
converterRefreshFail: "Le nom des champs et l'élement du DOM receveur sont nécessaires à l'affichage des données.", converterRefreshFail: "Le nom des champs et l'élement du DOM receveur sont nécessaires à l'affichage des données.",
filterCheckIsOkFail: "Le test est lancé sur un filtre incorrectement initialisé ou sur un attribut absent de la donnée à tester.",
filterNeedDatas: "Le création d'un filtre nécessite la présence des données à filtrer.", filterNeedDatas: "Le création d'un filtre nécessite la présence des données à filtrer.",
pagination2HTMLFail: "Toutes les donnée nécessaires à la création des sélecteurs de pagination n'ont pas été fournies.", pagination2HTMLFail: "Toutes les donnée nécessaires à la création des sélecteurs de pagination n'ont pas été fournies.",
paginationNeedByfaultValueBeInOptions: "La valeur de pagination par défaut doit faire partie des options proposées.", paginationNeedByfaultValueBeInOptions: "La valeur de pagination par défaut doit faire partie des options proposées.",

View File

@ -4,7 +4,6 @@ module.exports =
converterFieldNotFound : "Le champ n'existe pas dans les données ou les données n'ont pas encore été chargées.", converterFieldNotFound : "Le champ n'existe pas dans les données ou les données n'ont pas encore été chargées.",
converterNeedDatasElt: "Merci de fournir un id valide pour l'élément où afficher les données.", converterNeedDatasElt: "Merci de fournir un id valide pour l'élément où afficher les données.",
converterRefreshFail: "Le nom des champs et l'élement du DOM receveur sont nécessaires à l'affichage des données.", converterRefreshFail: "Le nom des champs et l'élement du DOM receveur sont nécessaires à l'affichage des données.",
filterCheckIsOkFail: "Le test est lancé sur un filtre incorrectement initialisé ou sur un attribut absent de la donnée à tester.",
filterNeedDatas: "Le création d'un filtre nécessite la présence des données à filtrer.", filterNeedDatas: "Le création d'un filtre nécessite la présence des données à filtrer.",
pagination2HTMLFail : "Toutes les donnée nécessaires à la création des sélecteurs de pagination n'ont pas été fournies.", pagination2HTMLFail : "Toutes les donnée nécessaires à la création des sélecteurs de pagination n'ont pas été fournies.",
paginationNeedByfaultValueBeInOptions: "La valeur de pagination par défaut doit faire partie des options proposées.", paginationNeedByfaultValueBeInOptions: "La valeur de pagination par défaut doit faire partie des options proposées.",

View File

@ -85,7 +85,7 @@ export interface Selectors extends Filters
{ {
datasFieldNb: number; datasFieldNb: number;
name: string; name: string;
selectedValue: number|undefined; selectedValues: number[];
separator: string|undefined; separator: string|undefined;
values: string[]; values: string[];
} }

View File

@ -116,4 +116,4 @@ Z (numéro atomique),Élément,Symbole,Famille,Abondance des éléments dans la
8,Oxygène,O,Non-métal,> 100000 8,Oxygène,O,Non-métal,> 100000
15,Phosphore,P,Non-métal,> 100000 15,Phosphore,P,Non-métal,> 100000
16,Soufre,S,,> 100000 16,Soufre,S,,> 100000
34,Sélénium,Se,,> 1 et < 100 000 34,Sélénium,Se,,> 1 et < 100 000
1 Z (numéro atomique) Élément Symbole Famille Abondance des éléments dans la croûte terrestre (μg/k)
116 8 Oxygène O Non-métal > 100000
117 15 Phosphore P Non-métal > 100000
118 16 Soufre S > 100000
119 34 Sélénium Se > 1 et < 100 000

View File

@ -1,12 +1,10 @@
module.exports = module.exports =
{ {
datasViewEltHTML: '<div id="fixture"><div id="selector1"></div><div id="selector2"></div><div id="paginationOptions"></div><div id="counter"></div><div id="datas"></div><div id="pages"></div><div id="mySearch"></div></div>', datasViewEltHTML: '<div id="fixture"><div id="selector1"></div><div id="selector2"></div><div id="paginationOptions"></div><div id="counter"></div><div id="datas"></div><div id="pages"></div><div id="mySearch"></div></div>',
selector1HTML: '<label for="freeDatas2HTML_selector1">Famille :</label><select name="freeDatas2HTML_selector1" id="freeDatas2HTML_selector1"><option value="0">----</option><option value="1">Actinide</option><option value="2">Gaz noble</option><option value="3">gaz rare</option><option value="4">Halogène</option><option value="5">Indéfinie</option><option value="6">Lanthanide</option><option value="7">Métal alcalin</option><option value="8">Métal alcalino-terreux</option><option value="9">Métal de transition</option><option value="10">Métal pauvre</option><option value="11">Métalloïde</option><option value="12">Non-métal</option></select>', selector1HTML: '<label for="freeDatas2HTML_selector1">Famille :</label><select name="freeDatas2HTML_selector1" id="freeDatas2HTML_selector1"><option value="0">----</option><option value="1">Actinide</option><option value="2">Gaz noble</option><option value="3">gaz rare</option><option value="4">Halogène</option><option value="5">Indéfinie</option><option value="6">Lanthanide</option><option value="7">Métal alcalin</option><option value="8">Métal alcalino-terreux</option><option value="9">Métal de transition</option><option value="10">Métal pauvre</option><option value="11">Métalloïde</option><option value="12">Non-métal</option></select>',
selector2HTML: '<label for="freeDatas2HTML_selector2">Abondance des éléments dans la croûte terrestre (μg/k) :</label><select name="freeDatas2HTML_selector2" id="freeDatas2HTML_selector2"><option value="0">----</option><option value="1">&gt; 1 et &lt; 100 000</option><option value="2">&gt; 100000</option><option value="3">≤ 1</option><option value="4">Inexistant</option><option value="5">Traces</option></select>', selector2HTML: '<label for="freeDatas2HTML_selector2">Abondance des éléments dans la croûte terrestre (μg/k) :</label><select name="freeDatas2HTML_selector2" id="freeDatas2HTML_selector2"><option value="0">----</option><option value="1">&gt; 1 et &lt; 100 000</option><option value="2">&gt; 100000</option><option value="3">≤ 1</option><option value="4">Inexistant</option><option value="5">Traces</option></select>',
selector2HTMLWithLabel: '<label for="freeDatas2HTML_selector2">Abondance des éléments :</label><select name="freeDatas2HTML_selector2" id="freeDatas2HTML_selector2"><option value="0">----</option><option value="1">&gt; 1 et &lt; 100 000</option><option value="2">&gt; 100000</option><option value="3">≤ 1</option><option value="4">Inexistant</option><option value="5">Traces</option></select>', selector2HTMLWithLabel: '<label for="freeDatas2HTML_selector2">Abondance des éléments :</label><select name="freeDatas2HTML_selector2" id="freeDatas2HTML_selector2"><option value="0">----</option><option value="1">&gt; 1 et &lt; 100 000</option><option value="2">&gt; 100000</option><option value="3">≤ 1</option><option value="4">Inexistant</option><option value="5">Traces</option></select>',
selector1HTMLWithSeparator: '<label for="freeDatas2HTML_selector1">Étiquettes :</label><select name="freeDatas2HTML_selector1" id="freeDatas2HTML_selector1"><option value="0">----</option><option value="1">Exemple0</option><option value="2">Exemple1</option><option value="3">Exemple2</option><option value="4">Exemple3</option><option value="5">Exemple4</option><option value="6">Exemple5</option><option value="7">Exemple6</option><option value="8">Exemple7</option><option value="9">Exemple8</option><option value="10">Exemple9</option><option value="11">Exemple10</option></select>', selector2HTMLWithMultiple: '<label for="freeDatas2HTML_selector2">Abondance des éléments dans la croûte terrestre (μg/k) :</label><select name="freeDatas2HTML_selector2" id="freeDatas2HTML_selector2" multiple=""><option value="0">----</option><option value="1">&gt; 1 et &lt; 100 000</option><option value="2">&gt; 100000</option><option value="3">≤ 1</option><option value="4">Inexistant</option><option value="5">Traces</option></select>',
selectorHTMLWithTrim: '<label for="freeDatas2HTML_selector1">Famille :</label><select name="freeDatas2HTML_selector1" id="freeDatas2HTML_selector1"><option value="0">----</option><option value="1">Gaz noble</option><option value="2">Métal alcalin</option><option value="3">Métal alcalino-terreux</option><option value="4">Métalloïde</option><option value="5">Non-métal</option></select>',
selector1HTMLWithFunction: '<label for="freeDatas2HTML_selector1">Abondance des éléments dans la croûte terrestre (μg/k) :</label><select name="freeDatas2HTML_selector1" id="freeDatas2HTML_selector1"><option value="0">----</option><option value="1">Inexistant</option><option value="2">Traces</option><option value="3">≤ 1</option><option value="4">&gt; 1 et &lt; 100 000</option><option value="5">&gt; 100000</option></select>',
selectorHTMLWithFakeItem: '<label for="freeDatas2HTML_selector1">Famille :</label><select name="freeDatas2HTML_selector1" id="freeDatas2HTML_selector1"><option value="0">----</option><option value="1">Actinide</option><option value="2">Gaz noble</option><option value="3">gaz rare</option><option value="4">Halogène</option><option value="5">Indéfinie</option><option value="6">Lanthanide</option><option value="7">Métal alcalin</option><option value="8">Métal alcalino-terreux</option><option value="9">Métal de transition</option><option value="10">Métal pauvre</option><option value="11">Métalloïde</option><option value="12">Non-métal</option><option value="13">Je suis un gros fake !</option></select>', selectorHTMLWithFakeItem: '<label for="freeDatas2HTML_selector1">Famille :</label><select name="freeDatas2HTML_selector1" id="freeDatas2HTML_selector1"><option value="0">----</option><option value="1">Actinide</option><option value="2">Gaz noble</option><option value="3">gaz rare</option><option value="4">Halogène</option><option value="5">Indéfinie</option><option value="6">Lanthanide</option><option value="7">Métal alcalin</option><option value="8">Métal alcalino-terreux</option><option value="9">Métal de transition</option><option value="10">Métal pauvre</option><option value="11">Métalloïde</option><option value="12">Non-métal</option><option value="13">Je suis un gros fake !</option></select>',
sortingColumn1HTML: '<a href="#freeDatas2HTMLSorting0" id="freeDatas2HTMLSorting0">Z (numéro atomique)</a>', sortingColumn1HTML: '<a href="#freeDatas2HTMLSorting0" id="freeDatas2HTMLSorting0">Z (numéro atomique)</a>',
sortingColumn2HTML: '<a href="#freeDatas2HTMLSorting2" id="freeDatas2HTMLSorting2">Symbole</a>', sortingColumn2HTML: '<a href="#freeDatas2HTMLSorting2" id="freeDatas2HTMLSorting2">Symbole</a>',

View File

@ -6,7 +6,7 @@ describe("Test des sélecteurs de données", () =>
{ {
let converter: FreeDatas2HTML; let converter: FreeDatas2HTML;
let selector: Selector; let selector: Selector;
let selectElement : HTMLInputElement; let selectElement : HTMLSelectElement;
beforeEach( async () => beforeEach( async () =>
{ {
@ -22,9 +22,9 @@ describe("Test des sélecteurs de données", () =>
document.body.removeChild(document.getElementById("fixture")); document.body.removeChild(document.getElementById("fixture"));
}); });
describe("Test des données de configuration.", () => describe("Test des données d'initialisation.", () =>
{ {
it("Doit générer une erreur, si initialisé sans avoir au préalable charger des données.", async () => it("Doit générer une erreur, si initialisé sans avoir au préalable chargé des données.", async () =>
{ {
// Convertisseur non lancé : // Convertisseur non lancé :
converter=new FreeDatas2HTML("CSV"); converter=new FreeDatas2HTML("CSV");
@ -42,6 +42,13 @@ describe("Test des sélecteurs de données", () =>
expect(() => { return new Selector(converter, -1, { id:"selector1" }); }).toThrowError(errors.selectorFieldNotFound); expect(() => { return new Selector(converter, -1, { id:"selector1" }); }).toThrowError(errors.selectorFieldNotFound);
expect(() => { return new Selector(converter, 1.1, { id:"selector1" }); }).toThrowError(errors.selectorFieldNotFound); expect(() => { return new Selector(converter, 1.1, { id:"selector1" }); }).toThrowError(errors.selectorFieldNotFound);
}); });
it("Doit générer une erreur, si aucune donnée n'a été trouvée dans le champ du filtre.", async () =>
{
converter.parser.setRemoteSource({ url:"http://localhost:9876/datas/datasEmptyField.csv" });
await converter.run();
expect(() => { return new Selector(converter, 3, { id:"selector1" });}).toThrowError(errors.selectorFieldIsEmpty);
});
it("Si un séparateur vide est fourni pour un filtre, il doit être ignoré.", () => it("Si un séparateur vide est fourni pour un filtre, il doit être ignoré.", () =>
{ {
@ -49,12 +56,58 @@ describe("Test des sélecteurs de données", () =>
expect(selector.separator).toBeUndefined(); expect(selector.separator).toBeUndefined();
}); });
it("Si toutes les paramètres sont valides, ils doivent être acceptés.", () => it("Si toutes les paramètres sont valides, ils doivent être acceptés et les informations correctement récupérées.", () =>
{ {
expect(() => { selector=new Selector(converter, 2, { id:"selector1" }, ","); return true; }).not.toThrowError(); expect(() => { selector=new Selector(converter, 3, { id:"selector1" }, ","); return true; }).not.toThrowError();
expect(selector.datasFieldNb).toEqual(2); expect(selector.datasFieldNb).toEqual(3);
expect(selector.datasViewElt).toEqual({ id:"selector1", eltDOM:document.getElementById("selector1") }); expect(selector.datasViewElt).toEqual({ id:"selector1", eltDOM:document.getElementById("selector1") });
expect(selector.separator).toEqual(","); expect(selector.separator).toEqual(",");
expect(selector.name).toEqual("Famille");
expect(selector.values).toEqual(["Actinide","Gaz noble","gaz rare","Halogène","Indéfinie","Lanthanide","Métal alcalin","Métal alcalino-terreux","Métal de transition","Métal pauvre","Métalloïde","Non-métal"]);
});
it("Si des valeurs vides sont présentes dans un champ utilisé pour un filtre, elles doivent être ignorées.", async () =>
{
converter.parser.setRemoteSource({ url:"http://localhost:9876/datas/datas1-emtyinfield.csv" });
await converter.run();
selector=new Selector(converter, 3, { id:"selector1" }, "");
expect(selector.values).toEqual(["Actinide","Gaz noble","gaz rare","Halogène","Indéfinie","Lanthanide","Métal alcalin","Métal alcalino-terreux","Métal de transition","Métal pauvre","Métalloïde","Non-métal"]);
});
it("Si des espaces entourent certaines valeurs pour ce champ, ils doivent être supprimés.", async () =>
{
converter.parser.setRemoteSource({ url:"http://localhost:9876/datas/datasNeedTrim.csv" });
await converter.run();
selector=new Selector(converter, 3, { id:"selector1" }, "");
expect(selector.values).toEqual(["Gaz noble","Métal alcalin","Métal alcalino-terreux","Métalloïde","Non-métal"]);
});
it("Si un séparateur est fourni, les valeurs distinctes extraites de ce champ doivent le prendre en compte.", async () =>
{
converter.parser.setRemoteSource({ url:"http://localhost:9876/datas/datas1+tagsfield.csv" });
await converter.run();
selector=new Selector(converter, 5, { id:"selector1" }, "|");
selector.filter2HTML();
expect(selector.values).toEqual(["Exemple0","Exemple1","Exemple2","Exemple3","Exemple4","Exemple5","Exemple6","Exemple7","Exemple8","Exemple9","Exemple10"]);
});
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=new Selector(converter, 4, { id:"selector1" });
expect(selector.values).toEqual(["Inexistant","Traces","≤ 1","> 1 et < 100 000","> 100000"]);
}); });
}); });
@ -81,131 +134,143 @@ describe("Test des sélecteurs de données", () =>
expect(document.getElementById("selector2").innerHTML).toEqual(fixtures.selector2HTMLWithLabel); expect(document.getElementById("selector2").innerHTML).toEqual(fixtures.selector2HTMLWithLabel);
}); });
it("Si des valeurs vides sont présentes dans une champ utilisé pour un sélecteur, elles doivent être ignorées.", async () => it("Doit prendre en compte la possibilité de sélectionner plusieurs valeurs.", () =>
{ {
selector.converter.parser.setRemoteSource({ url:"http://localhost:9876/datas/datas1-emtyinfield.csv" }); selector=new Selector(converter, 4, { id:"selector2" });
await selector.converter.run(); selector.isMultiple=true;
selector.filter2HTML();
expect(document.getElementById("selector1").innerHTML).toEqual(fixtures.selector1HTML);
});
it("Si des espaces entourent certaines valeurs pour ce champ, ils doivent être supprimés.", async () =>
{
selector.converter.parser.setRemoteSource({ url:"http://localhost:9876/datas/datasNeedTrim.csv" });
await selector.converter.run();
selector.filter2HTML(); selector.filter2HTML();
expect(document.getElementById("selector1").innerHTML).toEqual(fixtures.selectorHTMLWithTrim); expect(document.getElementById("selector2").innerHTML).toEqual(fixtures.selector2HTMLWithMultiple);
}); // le 'multiple' ajouté est transformé en 'multiple=""' par le navigateur.
it("Si un séparateur est fourni, les valeurs distinctes extraites de ce champ doivent le prendre en compte.", async () =>
{
converter.parser.setRemoteSource({ url:"http://localhost:9876/datas/datas1+tagsfield.csv" });
await converter.run();
selector=new Selector(converter, 5, { id:"selector1" }, "|");
selector.filter2HTML();
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=new Selector(converter, 4, { id:"selector1" });
selector.filter2HTML();
expect(document.getElementById("selector1").innerHTML).toEqual(fixtures.selector1HTMLWithFunction);
});
it("Doit générer une erreur, si aucune donnée n'a été trouvée pour créer le <SELECT>.", async () =>
{
selector.converter.parser.setRemoteSource({ url:"http://localhost:9876/datas/datasEmptyField.csv" });
await selector.converter.run();
expect(() => { return selector.filter2HTML(); }).toThrowError(errors.selectorFieldIsEmpty);
}); });
}); });
describe("Manipulation des sélecteurs et filtre des données.", () => describe("Manipulation des sélecteurs et filtre des données.", () =>
{ {
beforeEach( async () => beforeEach( async () =>
{ {
selector=new Selector(converter, 3, { id:"selector1" }); // filtre sur le champ "famille" selector=new Selector(converter, 3, { id:"selector1" }); // filtre sur le champ "famille"
selector.filter2HTML(); selector.filter2HTML();
selectElement=document.getElementById("freeDatas2HTML_selector1") as HTMLInputElement; selectElement=document.getElementById("freeDatas2HTML_selector1") as HTMLSelectElement;
}); });
it("La manipulation d'un sélecteur doit enregistrer la valeur sélectionnée et appeler la fonction actualisant l'affichage.", () => it("La manipulation d'un sélecteur doit enregistrer la valeur sélectionnée et appeler la fonction actualisant l'affichage.", () =>
{ {
spyOn(converter, "refreshView"); spyOn(converter, "refreshView");
selectElement.value="4"; selectElement.value="4";
selectElement.dispatchEvent(new Event('change')); selectElement.dispatchEvent(new Event("change"));
expect(selector.selectedValue).toEqual(3); expect(selector.selectedValues[0]).toEqual(3);
expect(converter.refreshView).toHaveBeenCalledTimes(1); expect(converter.refreshView).toHaveBeenCalledTimes(1);
selectElement.value="0"; // 0 = annulation de ce filtre, puisqu'aucune valeur choisie // Pas de choix multiples, donc seul le dernier choix est gardé :
selectElement.dispatchEvent(new Event('change')); selectElement.value="2";
expect(selector.selectedValue).toBeUndefined(); selectElement.dispatchEvent(new Event("change"));
expect(selector.selectedValues[0]).toEqual(1);
expect(converter.refreshView).toHaveBeenCalledTimes(2); expect(converter.refreshView).toHaveBeenCalledTimes(2);
});
selectElement.value="0"; // 0 = annulation de ce filtre, puisqu'aucune valeur choisie
it("Doit générer une erreur si une donnée est testée sur un sélecteur non affiché dans la page.", () => selectElement.dispatchEvent(new Event("change"));
{ expect(selector.selectedValues.length).toEqual(0);
selector=new Selector(converter, 3, { id:"selector1" }); expect(converter.refreshView).toHaveBeenCalledTimes(3);
expect(() => { selector.dataIsOk({ "nom" : "oui" }); }).toThrowError(errors.filterCheckIsOkFail);
}); });
it("Si plusieurs choix sont autorisés, toutes les valeurs doivent être enregistrées, l'affichage s'actualisant à chaque fois.", () =>
{
spyOn(converter, "refreshView");
selector=new Selector(converter, 3, { id:"selector1" });
selector.isMultiple=true;
selector.filter2HTML();
selectElement=document.getElementById("freeDatas2HTML_"+selector.datasViewElt.id) as HTMLSelectElement;
selectElement.options[4].selected=true;
selectElement.dispatchEvent(new Event("change"));
expect(selector.selectedValues[0]).toEqual(3);
expect(converter.refreshView).toHaveBeenCalledTimes(1);
// Le choix d'une nouvelle option ne supprime pas les précédents choix
// Mais les valeurs sélectionnées sont toujours parcourues dans l'ordre du SELECT
selectElement.options[3].selected=true;
selectElement.dispatchEvent(new Event("change"));
expect(selector.selectedValues[0]).toEqual(2);
expect(selector.selectedValues[1]).toEqual(3);
expect(converter.refreshView).toHaveBeenCalledTimes(2);
// Le choix 0 annule les précédents choix
selectElement.options[0].selected=true;
selectElement.dispatchEvent(new Event("change"));
expect(selector.selectedValues.length).toEqual(0);
expect(converter.refreshView).toHaveBeenCalledTimes(3);
});
it("Doit toujours retourner true si aucune des valeurs du filtre n'est sélectionnée.", () => it("Doit toujours retourner true si aucune des valeurs du filtre n'est sélectionnée.", () =>
{ {
// Le filtre est sur 0 par défaut // Le filtre est sur 0 par défaut
expect(selector.dataIsOk({ "nom" : "oui" })).toBeTrue(); expect(selector.dataIsOk({ "nom" : "oui" })).toBeTrue();
// Même comportement après un retour : // Même comportement après un retour :
selectElement.value="2"; selectElement.value="2";
selectElement.dispatchEvent(new Event('change')); selectElement.dispatchEvent(new Event("change"));
selectElement.value="0"; selectElement.value="0";
selectElement.dispatchEvent(new Event('change')); selectElement.dispatchEvent(new Event("change"));
expect(selector.dataIsOk({ "nom" : "oui" })).toBeTrue(); expect(selector.dataIsOk({ "nom" : "oui" })).toBeTrue();
}); });
it("Doit générer une erreur si la valeur sélectionnée n'est pas trouvé dans la liste des valeurs connues.", () =>
{
selectElement.innerHTML=fixtures.selectorHTMLWithFakeItem;
selectElement.value="13";
selectElement.dispatchEvent(new Event('change'));
expect(() => { selector.dataIsOk({ "nom" : "oui" }); }).toThrowError(errors.selectorSelectedIndexNotFound);
});
it("Doit retourner false, si la donnée testée ne possède pas le champ sur lequel les données sont filtrées.", () => it("Doit retourner false, si la donnée testée ne possède pas le champ sur lequel les données sont filtrées.", () =>
{ {
selectElement.value="2"; selectElement.value="2";
selectElement.dispatchEvent(new Event('change')); selectElement.dispatchEvent(new Event("change"));
expect(selector.dataIsOk({ "nom" : "rémi sans famille" })).toBeFalse(); expect(selector.dataIsOk({ "nom" : "rémi sans famille" })).toBeFalse();
}); });
it("Doit générer une erreur si la valeur sélectionnée n'est pas trouvée dans la liste des valeurs connues.", () =>
{
selectElement.innerHTML=fixtures.selectorHTMLWithFakeItem;
selectElement.value="13";
selectElement.dispatchEvent(new Event("change"));
expect(() => { selector.dataIsOk({ "Famille" : "nombreuse" }); }).toThrowError(errors.selectorSelectedIndexNotFound);
});
it("Doit retourner false, si une donnée testée ne correspond pas à la valeur sélectionnée pour le filtre.", async () => it("Doit retourner false, si une donnée testée ne correspond pas à la valeur sélectionnée pour le filtre.", async () =>
{ {
selectElement.value="4"; // = Halogène selectElement.value="4"; // = Halogène
selectElement.dispatchEvent(new Event('change')); selectElement.dispatchEvent(new Event("change"));
expect(selector.dataIsOk({ "Famille": "Hallo Eugène !" })).toBeFalse(); expect(selector.dataIsOk({ "Famille": "Hallo Eugène !" })).toBeFalse();
}); });
it("Doit retourner true, si une donnée testée correspond à la valeur sélectionnée pour ce filtre.", () => it("Doit retourner true, si une donnée testée correspond à la valeur sélectionnée pour ce filtre.", () =>
{ {
selectElement.value="4"; selectElement.value="4";
selectElement.dispatchEvent(new Event('change')); selectElement.dispatchEvent(new Event("change"));
expect(selector.dataIsOk({ "Famille": "Halogène" })).toBeTrue(); expect(selector.dataIsOk({ "Famille": "Halogène" })).toBeTrue();
// Y compris si entouré d'espaces : // Y compris si entouré d'espaces :
expect(selector.dataIsOk({ "Famille": " Halogène " })).toBeTrue(); expect(selector.dataIsOk({ "Famille": " Halogène " })).toBeTrue();
}); });
it("Doit retourner false, si une donnée testée ne correspond à aucune des valeurs sélectionnées dans un filtre multichoix.", async () =>
{
selector=new Selector(converter, 3, { id:"selector1" });
selector.isMultiple=true;
selector.filter2HTML();
selectElement=document.getElementById("freeDatas2HTML_"+selector.datasViewElt.id) as HTMLSelectElement;
selectElement.options[3].selected=true;// sélection = gaz rare
selectElement.options[4].selected=true; // sélection = Halogène
selectElement.dispatchEvent(new Event("change"));
expect(selector.dataIsOk({ "Famille": "Hallo Eugène !" })).toBeFalse();
});
it("Doit retourner true, si une donnée testée correspond à au moins une des valeurs sélectionnées dans un filtre multichoix.", () =>
{
selector=new Selector(converter, 3, { id:"selector1" });
selector.isMultiple=true;
selector.filter2HTML();
selectElement=document.getElementById("freeDatas2HTML_"+selector.datasViewElt.id) as HTMLSelectElement;
selectElement.options[3].selected=true;// sélection = gaz rare
selectElement.options[4].selected=true; // sélection = Halogène
selectElement.dispatchEvent(new Event("change"));
expect(selector.dataIsOk({ "Famille": "Halogène" })).toBeTrue();
// Y compris si entouré d'espaces :
expect(selector.dataIsOk({ "Famille": " gaz rare " })).toBeTrue();
});
}); });
describe("Manipulation des sélecteurs avec séparateur.", () => describe("Manipulation des sélecteurs avec séparateur.", () =>
@ -216,27 +281,27 @@ describe("Test des sélecteurs de données", () =>
await converter.run(); await converter.run();
selector=new Selector(converter, 5, { id:"selector1" }, "|"); // filtre sur le champ "Étiquettes" selector=new Selector(converter, 5, { id:"selector1" }, "|"); // filtre sur le champ "Étiquettes"
selector.filter2HTML(); selector.filter2HTML();
selectElement=document.getElementById("freeDatas2HTML_selector1") as HTMLInputElement; selectElement=document.getElementById("freeDatas2HTML_selector1") as HTMLSelectElement;;
}); });
it("Doit retourner false, si la donnée testée ne possède pas le champ sur lequel les données sont filtrées.", () => it("Doit retourner false, si la donnée testée ne possède pas le champ sur lequel les données sont filtrées.", () =>
{ {
selectElement.value="2"; selectElement.value="2";
selectElement.dispatchEvent(new Event('change')); selectElement.dispatchEvent(new Event("change"));
expect(selector.dataIsOk({ "nom" : "oui" })).toBeFalse(); expect(selector.dataIsOk({ "nom" : "oui" })).toBeFalse();
}); });
it("Doit retourner false, si une donnée testée ne correspond pas à la valeur sélectionnée pour le filtre.", () => it("Doit retourner false, si une donnée testée ne correspond pas à la valeur sélectionnée pour le filtre.", () =>
{ {
selectElement.value="4"; // = Exemple3 selectElement.value="4"; // = Exemple3
selectElement.dispatchEvent(new Event('change')); selectElement.dispatchEvent(new Event("change"));
expect(selector.dataIsOk({ "Étiquettes": "Mauvais exemple" })).toBeFalse(); expect(selector.dataIsOk({ "Étiquettes": "Mauvais exemple" })).toBeFalse();
}); });
it("Doit retourner true, si une donnée testée correspond pas à la valeur sélectionnée pour ce filtre.", () => it("Doit retourner true, si une donnée testée correspond pas à la valeur sélectionnée pour ce filtre.", () =>
{ {
selectElement.value="4"; selectElement.value="4";
selectElement.dispatchEvent(new Event('change')); selectElement.dispatchEvent(new Event("change"));
expect(selector.dataIsOk({ "Étiquettes": "Exemple3" })).toBeTrue(); expect(selector.dataIsOk({ "Étiquettes": "Exemple3" })).toBeTrue();
// Même quand elle n'est pas seule : // Même quand elle n'est pas seule :
expect(selector.dataIsOk({ "Étiquettes": "Exemple3|Exemple1|Exemple9" })).toBeTrue(); expect(selector.dataIsOk({ "Étiquettes": "Exemple3|Exemple1|Exemple9" })).toBeTrue();