Compare commits

...

3 Commits

17 changed files with 340 additions and 215 deletions

View File

@ -1,6 +1,6 @@
{
"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.",
"main": "index.js",
"scripts": {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -43,7 +43,9 @@
<article>
<p>Cet exemple dutilisation de FreeDatas2HTML est assez complet en montrant comment à partir dun fichier de données, il est possible de proposer:</p>
<ul>
<li>Des <b>listes permettant de filtrer les données</b>. La troisième liste (Mots-clés) démontrant quil est possible d<b>extraire des données dun champ ayant plusieurs valeurs par ligne</b>. Ici, les valeurs distinctes sont séparées par des virgules, mais nimporte quel autre caractère séparateur peut être désigné. Les sélections dans les différentes listes sont combinées pour ne garder que les résultats validant tous les choix.</li>
<li>Des <b>listes permettant de filtrer les données</b>. La troisième liste (Mots-clés) démontrant quil est possible d<b>extraire des données dun champ ayant plusieurs valeurs par ligne</b>. Ici, les valeurs distinctes sont séparées par des virgules, mais nimporte quel autre caractère séparateur peut être désigné.</li>
<li>Les sélections dans les différentes listes sont combinées pour ne garder que les résultats validant tous les choix.</li>
<li>Dans la troisième liste, <b>il est possible de sélectionner simultanément plusieurs valeurs</b> (en appuyant sur «Ctrl» sur PC). Ici, il suffit quau moins une des valeurs sélectionnées soient trouvée, pour quune ligne soit affichée.</li>
<li>Des champs (=colonnes) permettant de <b>classer les données</b>: un 1ᵉʳ clic lance un classement par ordre croissant, le 2ᵉ pour ordre décroissant et ainsi de suite. Remarquez que le champ «Abondance…» utilise une fonction spécifique, car un classement par ordre alphabétique naurait pas convenu ici.</li>
<li>Un <b>compteur affichant le nombre total de résultats</b>, avec prise en compte des éventuels filtres utilisés.</li>
<li>Des <b>options de pagination</b>.</li>

View File

@ -8,13 +8,14 @@ export class Selector implements Selectors
private _converter: FreeDatas2HTML;
private _datasFieldNb: number;
private _datasViewElt: DOMElement={ id: "", eltDOM: undefined };
private _selectedValue: number|undefined=undefined;
private _selectedValues: number[]=[];
private _separator: string|undefined;
private _values: 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
// Le champ duquel le sélecteur tire ses données doit exister ?
// 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.
constructor(converter: FreeDatas2HTML, datasFieldNb: number, elt: DOMElement, separator?: string)
{
if(converter.fields.length === 0 || converter.datas.length === 0)
@ -29,6 +30,8 @@ export class Selector implements Selectors
// Pas de trim(), car l'espace peut être le séparateur :
if(separator !== undefined && 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;
}
get selectedValue() : number|undefined
get selectedValues() : number[]
{
return this._selectedValue;
return this._selectedValues;
}
get separator() : string|undefined
@ -67,23 +70,22 @@ export class Selector implements Selectors
return this._values;
}
// Création du <select> dans le DOM correspondant au filtre
public filter2HTML(label:string="") : void
// Dédoublonnage et classement des valeurs disponibles pour le champ du sélecteur
private setValues() :void
{
this._name=this._converter.fields![this._datasFieldNb]; // "!" car l'existence du champ est testé par le constructeur
for (let row of this._converter.datas)
for (const row of this._converter.datas)
{
let checkedValue;
if(this._separator === undefined)
{
checkedValue=row[this._name].trim(); // trim() évite des problèmes de classement des éléments du SELECT
checkedValue=row[this._name].trim(); // trim() évite des problèmes de classement des éléments du SELECT.
if(checkedValue !== "" && this._values.indexOf(checkedValue) === -1)
this._values.push(checkedValue);
}
else
{
let checkedValues=row[this._name].split(this._separator);
for(let value of checkedValues)
for(const value of checkedValues)
{
checkedValue=value.trim();
if(checkedValue !== "" && this._values.indexOf(checkedValue) === -1)
@ -91,8 +93,8 @@ 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);
else
{
@ -101,65 +103,96 @@ export class Selector implements Selectors
this._values.sort(this._converter.getSortingFunctionForField(this._datasFieldNb)!.sort); // sans le "!" : TS2532: Object is possibly 'undefined' ???
else
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
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 :
if(this._name === "")
throw new Error(errors.filterCheckIsOkFail);
// Pas de valeur sélectionnée = pas de filtre sur ce champ
if(this._selectedValue === undefined)
return true;
if(this._values[this._selectedValue] === undefined) // théoriquement impossible, mais cela vient du client...
throw new Error(errors.selectorSelectedIndexNotFound);
// Si le champ est absent pour un enregistrement, il est refusé
if(data[this._name] === undefined)
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)
label=(label === "") ? this._name : label;
const multipleAttr=(this.isMultiple)? " multiple" :"";
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++)
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 HTMLSelectElement, mySelector=this;
selectElement.addEventListener("change", function(e)
{
mySelector._selectedValues=[];
if(mySelector.isMultiple)
{
if(value.trim() === selectedValueTxt)
for(let i=0; i < selectElement.selectedOptions.length; i++)
{
find=true;
break;
const selectedValue=parseInt(selectElement.selectedOptions[i].value,10);
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 () {
function Selector(converter, datasFieldNb, elt, separator) {
this._datasViewElt = { id: "", eltDOM: undefined };
this._selectedValue = undefined;
this._selectedValues = [];
this._values = [];
this._name = "";
this.isMultiple = false;
if (converter.fields.length === 0 || converter.datas.length === 0)
throw new Error(errors.filterNeedDatas);
else if (!converter.checkFieldExist(Number(datasFieldNb)))
@ -17,6 +18,8 @@ var Selector = (function () {
this._datasFieldNb = datasFieldNb;
if (separator !== undefined && separator !== "")
this._separator = separator;
this._name = this._converter.fields[this._datasFieldNb];
this.setValues();
}
}
Object.defineProperty(Selector.prototype, "converter", {
@ -47,9 +50,9 @@ var Selector = (function () {
enumerable: true,
configurable: true
});
Object.defineProperty(Selector.prototype, "selectedValue", {
Object.defineProperty(Selector.prototype, "selectedValues", {
get: function () {
return this._selectedValue;
return this._selectedValues;
},
enumerable: true,
configurable: true
@ -68,9 +71,7 @@ var Selector = (function () {
enumerable: true,
configurable: true
});
Selector.prototype.filter2HTML = function (label) {
if (label === void 0) { label = ""; }
this._name = this._converter.fields[this._datasFieldNb];
Selector.prototype.setValues = function () {
for (var _i = 0, _a = this._converter.datas; _i < _a.length; _i++) {
var row = _a[_i];
var checkedValue = void 0;
@ -96,50 +97,76 @@ var Selector = (function () {
this._values.sort(this._converter.getSortingFunctionForField(this._datasFieldNb).sort);
else
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) {
if (this._name === "")
throw new Error(errors.filterCheckIsOkFail);
if (this._selectedValue === undefined)
return true;
if (this._values[this._selectedValue] === undefined)
throw new Error(errors.selectorSelectedIndexNotFound);
if (data[this._name] === undefined)
return false;
var selectedValueTxt = this._values[this._selectedValue];
if (this._separator === undefined) {
if (data[this._name].trim() !== selectedValueTxt)
return false;
else
return true;
}
else {
var find = false;
var checkedValues = data[this._name].split(this._separator);
for (var _i = 0, checkedValues_2 = checkedValues; _i < checkedValues_2.length; _i++) {
var value = checkedValues_2[_i];
if (value.trim() === selectedValueTxt) {
find = true;
break;
Selector.prototype.filter2HTML = function (label) {
if (label === void 0) { label = ""; }
label = (label === "") ? this._name : label;
var multipleAttr = (this.isMultiple) ? " multiple" : "";
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>";
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 = document.getElementById("freeDatas2HTML_" + this._datasViewElt.id), mySelector = this;
selectElement.addEventListener("change", function (e) {
mySelector._selectedValues = [];
if (mySelector.isMultiple) {
for (var i = 0; i < selectElement.selectedOptions.length; i++) {
var selectedValue = parseInt(selectElement.selectedOptions[i].value, 10);
if (selectedValue === 0) {
mySelector._selectedValues = [];
break;
}
else
mySelector._selectedValues.push(selectedValue - 1);
}
}
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;
}());

View File

@ -87,6 +87,7 @@ var initialise = function () { return __awaiter(void 0, void 0, void 0, function
filtre2 = new Selector(converter, 4, { id: "filtre2" });
filtre2.filter2HTML();
filtre3 = new Selector(converter, 5, { id: "filtre3" }, ",");
filtre3.isMultiple = true;
filtre3.filter2HTML();
mySearch = new SearchEngine(converter, { id: "search" });
mySearch.label = "Qui cherche trouve ?";

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.",
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.",
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.",
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.",

View File

@ -56,7 +56,7 @@ const initialise=async () =>
pagination.selectedValue=10;
converter.pagination=pagination;
pagination.options2HTML();
// Création d'outils permettant de filtrer les données sur 3 champs différents :
let filtre1=new Selector(converter, 3, { id:"filtre1"} );
filtre1.filter2HTML();
@ -64,6 +64,7 @@ const initialise=async () =>
filtre2.filter2HTML();
// Le troisième devant prendre en compte un séparateur :
let filtre3=new Selector(converter, 5, { id:"filtre3"}, ",");
filtre3.isMultiple=true;
filtre3.filter2HTML();
// + Un moteur de recherche opérant sur tous les champs :

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.",
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.",
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.",
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.",

View File

@ -85,7 +85,7 @@ export interface Selectors extends Filters
{
datasFieldNb: number;
name: string;
selectedValue: number|undefined;
selectedValues: number[];
separator: string|undefined;
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
15,Phosphore,P,Non-métal,> 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 =
{
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>',
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>',
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>',
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>',
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>',
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 selector: Selector;
let selectElement : HTMLInputElement;
let selectElement : HTMLSelectElement;
beforeEach( async () =>
{
@ -22,14 +22,14 @@ describe("Test des sélecteurs de données", () =>
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é :
converter=new FreeDatas2HTML("CSV");
expect(() => { return new Selector(converter, 0, { id:"selector1" }); }).toThrowError(errors.filterNeedDatas);
// Note : les parseurs vont générer une erreur en amont s'ils ne trouvent pas de noms de champs.
// Note : les parseurs vont générer une erreur en amont s'ils ne trouvent pas de noms de champs
// Par contre, ils acceptent de ne pas trouver de données :
converter.parser.datas2Parse="Z (numéro atomique),Élément,Symbole,Famille,Abondance des éléments dans la croûte terrestre (μg/k)";
await converter.run();
@ -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.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é.", () =>
{
@ -49,12 +56,58 @@ describe("Test des sélecteurs de données", () =>
expect(selector.separator).toBeUndefined();
});
it("Si toutes les paramètres sont correctes, 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.datasFieldNb).toEqual(2);
expect(() => { selector=new Selector(converter, 3, { id:"selector1" }, ","); return true; }).not.toThrowError();
expect(selector.datasFieldNb).toEqual(3);
expect(selector.datasViewElt).toEqual({ id:"selector1", eltDOM:document.getElementById("selector1") });
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);
});
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" });
await selector.converter.run();
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=new Selector(converter, 4, { id:"selector2" });
selector.isMultiple=true;
selector.filter2HTML();
expect(document.getElementById("selector1").innerHTML).toEqual(fixtures.selectorHTMLWithTrim);
});
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);
expect(document.getElementById("selector2").innerHTML).toEqual(fixtures.selector2HTMLWithMultiple);
// le 'multiple' ajouté est transformé en 'multiple=""' par le navigateur.
});
});
describe("Manipulation des sélecteurs et filtre des données.", () =>
{
{
beforeEach( async () =>
{
selector=new Selector(converter, 3, { id:"selector1" }); // filtre sur le champ "famille"
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.", () =>
{
spyOn(converter, "refreshView");
selectElement.value="4";
selectElement.dispatchEvent(new Event('change'));
expect(selector.selectedValue).toEqual(3);
selectElement.value="4";
selectElement.dispatchEvent(new Event("change"));
expect(selector.selectedValues[0]).toEqual(3);
expect(converter.refreshView).toHaveBeenCalledTimes(1);
selectElement.value="0"; // 0 = annulation de ce filtre, puisqu'aucune valeur choisie
selectElement.dispatchEvent(new Event('change'));
expect(selector.selectedValue).toBeUndefined();
// Pas de choix multiples, donc seul le dernier choix est gardé :
selectElement.value="2";
selectElement.dispatchEvent(new Event("change"));
expect(selector.selectedValues[0]).toEqual(1);
expect(converter.refreshView).toHaveBeenCalledTimes(2);
});
it("Doit générer une erreur si une donnée est testée sur un sélecteur non affiché dans la page.", () =>
{
selector=new Selector(converter, 3, { id:"selector1" });
expect(() => { selector.dataIsOk({ "nom" : "oui" }); }).toThrowError(errors.filterCheckIsOkFail);
selectElement.value="0"; // 0 = annulation de ce filtre, puisqu'aucune valeur choisie
selectElement.dispatchEvent(new Event("change"));
expect(selector.selectedValues.length).toEqual(0);
expect(converter.refreshView).toHaveBeenCalledTimes(3);
});
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.", () =>
{
// Le filtre est sur 0 par défaut
expect(selector.dataIsOk({ "nom" : "oui" })).toBeTrue();
// Même comportement après un retour :
selectElement.value="2";
selectElement.dispatchEvent(new Event('change'));
selectElement.dispatchEvent(new Event("change"));
selectElement.value="0";
selectElement.dispatchEvent(new Event('change'));
selectElement.dispatchEvent(new Event("change"));
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.", () =>
{
selectElement.value="2";
selectElement.dispatchEvent(new Event('change'));
selectElement.dispatchEvent(new Event("change"));
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 () =>
{
selectElement.value="4"; // = Halogène
selectElement.dispatchEvent(new Event('change'));
selectElement.dispatchEvent(new Event("change"));
expect(selector.dataIsOk({ "Famille": "Hallo Eugène !" })).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 à la valeur sélectionnée pour ce filtre.", () =>
{
selectElement.value="4";
selectElement.dispatchEvent(new Event('change'));
selectElement.dispatchEvent(new Event("change"));
expect(selector.dataIsOk({ "Famille": "Halogène" })).toBeTrue();
// Y compris si entouré d'espaces :
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.", () =>
@ -216,33 +281,33 @@ describe("Test des sélecteurs de données", () =>
await converter.run();
selector=new Selector(converter, 5, { id:"selector1" }, "|"); // filtre sur le champ "Étiquettes"
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.", () =>
{
selectElement.value="2";
selectElement.dispatchEvent(new Event('change'));
selectElement.dispatchEvent(new Event("change"));
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.", () =>
{
selectElement.value="4"; // = Exemple3
selectElement.dispatchEvent(new Event('change'));
selectElement.dispatchEvent(new Event("change"));
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.", () =>
{
selectElement.value="4";
selectElement.dispatchEvent(new Event('change'));
selectElement.dispatchEvent(new Event("change"));
expect(selector.dataIsOk({ "Étiquettes": "Exemple3" })).toBeTrue();
// Même pas seule :
// Même quand elle n'est pas seule :
expect(selector.dataIsOk({ "Étiquettes": "Exemple3|Exemple1|Exemple9" })).toBeTrue();
expect(selector.dataIsOk({ "Étiquettes": "Exemple0|Exemple3|Exemple2" })).toBeTrue();
expect(selector.dataIsOk({ "Étiquettes": "Exemple0|Exemple4|Exemple3" })).toBeTrue();
// Y compris si entourée d'espaces :
// Ou entourée d'espaces :
expect(selector.dataIsOk({ "Étiquettes": "Exemple0|Exemple4| Exemple3 " })).toBeTrue();
expect(selector.dataIsOk({ "Étiquettes": " Exemple3 " })).toBeTrue();
});