Browse Source

Nouvelle version application avec ajout d'une extension créant des liens de classement stand alone (+ tests et build).

master
Fabrice PENHOËT 7 months ago
parent
commit
38637443e2
  1. 2
      package.json
  2. 229
      public/JS/exampleWithCSV.app.js
  3. 229
      public/JS/exampleWithHTML.app.js
  4. 229
      public/JS/exampleWithJSON.app.js
  5. 238
      public/JS/exampleWithUL.app.js
  6. 229
      public/JS/exampleWithUserFile.app.js
  7. 4
      src/build/SortingField.js
  8. 83
      src/build/demo/exampleWithUL.js
  9. 67
      src/build/extensions/SortingFieldsStandAlone.js
  10. 49
      src/extensions/SortingFieldsStandAlone.ts
  11. 108
      tests/extensions/sortingFieldsStandAloneSpec.ts

2
package.json

@ -1,6 +1,6 @@
{
"name": "freedatas2html",
"version": "1.0.0",
"version": "1.1.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": {

229
public/JS/exampleWithCSV.app.js

File diff suppressed because one or more lines are too long

229
public/JS/exampleWithHTML.app.js

File diff suppressed because one or more lines are too long

229
public/JS/exampleWithJSON.app.js

File diff suppressed because one or more lines are too long

238
public/JS/exampleWithUL.app.js

File diff suppressed because one or more lines are too long

229
public/JS/exampleWithUserFile.app.js

File diff suppressed because one or more lines are too long

4
src/build/SortingField.js

@ -1,4 +1,3 @@
var compare = require('natural-orderby').compare;
var errors = require("./errors.js");
var SortingField = (function () {
function SortingField(converter, datasFieldNb, fieldsDOMSelector) {
@ -39,6 +38,9 @@ var SortingField = (function () {
get: function () {
return this._order;
},
set: function (setting) {
this._order = setting;
},
enumerable: true,
configurable: true
});

83
src/build/demo/exampleWithUL.js

@ -0,0 +1,83 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
import { FreeDatas2HTML, Render, SortingField } from "../FreeDatas2HTML";
import { SortingFieldsStandAlone } from "../extensions/SortingFieldsStandAlone";
var initialise = function () { return __awaiter(void 0, void 0, void 0, function () {
var converter, myRender, sortingField1, sortingField2, sortingField3, allFields, e_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
converter = new FreeDatas2HTML("CSV");
converter.parser.setRemoteSource({ url: "http://localhost:8080/datas/elements-chimiques.csv" });
return [4, converter.run()];
case 1:
_a.sent();
converter.fields2Rend = [0, 1, 2, 3];
myRender = new Render();
myRender.settings =
{
allBegining: "<h4>Liste des éléments chimiques :</h4>",
allEnding: "",
linesBegining: "<ul>",
linesEnding: "</ul>",
lineBegining: "<li><ul>",
lineEnding: "</ul></li>",
dataDisplaying: "<li><b>#FIELDNAME :</b> #VALUE</li>",
};
converter.datasRender = myRender;
sortingField1 = new SortingField(converter, 0);
sortingField2 = new SortingField(converter, 1);
sortingField3 = new SortingField(converter, 2);
allFields = new SortingFieldsStandAlone(converter, { id: "classement" });
allFields.datasSortingFields = [sortingField1, sortingField2, sortingField3];
allFields.rendSettings.allBeginning = "<h4>#LABEL</h4><ul>";
allFields.rend2HTML("Champ sur lequel classer les données :");
converter.datasViewElt = { id: "datas" };
converter.refreshView();
return [3, 3];
case 2:
e_1 = _a.sent();
console.error(e_1);
document.getElementById("datas").innerHTML = "<div class=\"alert alert-warning\">D\u00E9sol\u00E9, mais un probl\u00E8me technique emp\u00EAche l'affichage des donn\u00E9es.</div>";
return [3, 3];
case 3: return [2];
}
});
}); };
console.log("Hello, ami développeur :-)\nLe code source TypeScript utilisé pour faire fonctionner cette page est lisible ici : :\nhttps://forge.chapril.org/Fab_Blab/freeDatas2HTML/src/branch/master/src/demo/exampleWithJSON.ts\nUn bug ? Une suggestion ? => fabrice@le-fab-lab.com");
initialise();

67
src/build/extensions/SortingFieldsStandAlone.js

@ -0,0 +1,67 @@
import { FreeDatas2HTML } from "../FreeDatas2HTML";
var errors = require("../errors.js");
errors.needSortingFields = "Vous devez fournir au moins un champ de classement valide.";
var SortingFieldsStandAlone = (function () {
function SortingFieldsStandAlone(converter, elt, settings) {
if (settings === void 0) { settings = SortingFieldsStandAlone.defaultSettings; }
this._datasViewElt = { id: "", eltDOM: undefined };
this.datasSortingFields = [];
if (converter.fields.length === 0)
throw new Error(errors.sortingFieldNeedDatas);
this._datasViewElt = FreeDatas2HTML.checkInDOMById(elt);
this._converter = converter;
this.rendSettings = settings;
}
Object.defineProperty(SortingFieldsStandAlone.prototype, "converter", {
get: function () {
return this._converter;
},
enumerable: true,
configurable: true
});
Object.defineProperty(SortingFieldsStandAlone.prototype, "datasViewElt", {
get: function () {
return this._datasViewElt;
},
enumerable: true,
configurable: true
});
SortingFieldsStandAlone.prototype.rend2HTML = function (label) {
if (label === void 0) { label = ""; }
if (this.datasSortingFields.length === 0)
throw new Error(errors.needSortingFields);
var htmlContent = this.rendSettings.allBeginning.replace("#LABEL", label);
for (var _i = 0, _a = this.datasSortingFields; _i < _a.length; _i++) {
var field = _a[_i];
htmlContent += this.rendSettings.fieldBeginning + "<a href='#freeDatas2HTMLSorting" + field.datasFieldNb + "' id='freeDatas2HTMLSorting" + field.datasFieldNb + "'>" + this._converter.fields[field.datasFieldNb] + "</a>" + this.rendSettings.fieldEnding;
}
htmlContent += this.rendSettings.allEnding;
this._datasViewElt.eltDOM.innerHTML = htmlContent;
var _loop_1 = function (field) {
var sortingLink = document.getElementById("freeDatas2HTMLSorting" + field.datasFieldNb);
sortingLink.addEventListener("click", function (e) {
e.preventDefault();
if (field.order === undefined || field.order === "desc")
field.order = "asc";
else
field.order = "desc";
field.converter.datasSortedField = field;
field.converter.refreshView();
});
};
for (var _b = 0, _c = this.datasSortingFields; _b < _c.length; _b++) {
var field = _c[_b];
_loop_1(field);
}
};
SortingFieldsStandAlone.defaultSettings = {
allBeginning: "<span>#LABEL</span><ul>",
allEnding: "</ul>",
fieldBeginning: "<li>",
fieldEnding: "</li>",
};
return SortingFieldsStandAlone;
}());
export { SortingFieldsStandAlone };
export { FreeDatas2HTML, SortingField } from "../FreeDatas2HTML";
export { errors };

49
src/extensions/SortingFieldsStandAlone.ts

@ -4,12 +4,13 @@
import { DOMElement } from "../interfaces";
import { FreeDatas2HTML, SortingField } from "../FreeDatas2HTML";
const errors=require("../errors.js");
errors.needSortingFields="Vous devez fournir au moins un champ de classement valide.";
export interface SortingFieldsSettings
interface SortingFieldsSettings
{
allBegining: string;
allBeginning: string;
allEnding: string;
fieldBegining: string;
fieldBeginning: string;
fieldEnding: string;
}
@ -17,45 +18,57 @@ export class SortingFieldsStandAlone
{
private _converter: FreeDatas2HTML;
private _datasViewElt: DOMElement={ id: "", eltDOM: undefined };
public datasSortingFields: SortingField[]=[];
public datasSortingFields: SortingField[]=[]; // SortingField refusera les champs non valides
public rendSettings: SortingFieldsSettings;
static readonly defaultSettings=
{
allBegining: "<ul>",
allBeginning: "<span>#LABEL</span><ul>",
allEnding: "</ul>",
fieldBegining: "<li>",
fieldBeginning: "<li>",
fieldEnding: "</li>",
};
constructor(converter: FreeDatas2HTML, elt: DOMElement, settings: SortingFieldsSettings=SortingFieldsStandAlone.defaultSettings)
{
// Ne peut être appelé avant d'avoir récupéré la liste des champs :
if(converter.fields.length === 0)
throw new Error(errors.sortingFieldNeedDatas);
this._converter=converter;
// Test l'existence dans le DOM de l'élément devant afficher les options de classement :
this._datasViewElt=FreeDatas2HTML.checkInDOMById(elt);
this._converter=converter;
this.rendSettings=settings;
}
get converter() : FreeDatas2HTML
{
return this._converter;
}
public rend2HTML(label:string="Classer les données suivant : ") : void
get datasViewElt() : DOMElement
{
// Il doit y avoir au moins un champ de classement fourni :
return this._datasViewElt;
}
public rend2HTML(label:string="") : void
{
// Au moins un champ de classement valide doit avoir été fourni :
if(this.datasSortingFields.length === 0)
throw new Error("Vous devez fournir au moins un champ de classement valide.");
throw new Error(errors.needSortingFields);
// Arrivé ici, je sais que j'ai au moins un lien de classement à injecter
let htmlContent="<label>"+label+"</label>"+this.rendSettings.allBegining;
// Arrivé ici, il y a au moins un lien de classement à injecter :
let htmlContent=this.rendSettings.allBeginning.replace("#LABEL", label);
for(let field of this.datasSortingFields)
{
htmlContent+=this.rendSettings.fieldBegining+"<a href='#freeDatas2HTMLSorting"+field.datasFieldNb+"' id='freeDatas2HTMLSorting"+field.datasFieldNb+"'>"+this._converter.fields[field.datasFieldNb]+"</a>"+this.rendSettings.fieldEnding;
htmlContent+=this.rendSettings.fieldBeginning+"<a href='#freeDatas2HTMLSorting"+field.datasFieldNb+"' id='freeDatas2HTMLSorting"+field.datasFieldNb+"'>"+this._converter.fields[field.datasFieldNb]+"</a>"+this.rendSettings.fieldEnding;
}
htmlContent+=this.rendSettings.allEnding;
this._datasViewElt.eltDOM!.innerHTML=htmlContent;// "!" car existence de l'élement, testé via le constructeur.
this._datasViewElt.eltDOM!.innerHTML=htmlContent;// "!" car existence de l'élement dans le DOM a été testé dans le constructeur.
// Les liens venant d'êtres injectés dans le DOM, il reste à les rendre actifs :
for(let field of this.datasSortingFields)
{
let sortingLink=document.getElementById("freeDatas2HTMLSorting"+field.datasFieldNb);
sortingLink!.addEventListener("click", function(e) // "!" car je sais que sortingLink existe, puisque je viens de le créer !
sortingLink!.addEventListener("click", function(e) // "!" car je sais que sortingLink existe, venant de le créer.
{
e.preventDefault();
if(field.order === undefined || field.order === "desc")
@ -67,4 +80,8 @@ export class SortingFieldsStandAlone
});
}
}
}
}
// Utile au script de tests :
export { FreeDatas2HTML, SortingField } from "../FreeDatas2HTML";
export { errors };

108
tests/extensions/sortingFieldsStandAloneSpec.ts

@ -0,0 +1,108 @@
import { errors, FreeDatas2HTML, SortingField, SortingFieldsStandAlone } from "../../src/extensions/SortingFieldsStandAlone";
const fixtures=require("../fixtures.js");
describe("Test des liens de classement (hors données).", () =>
{
let converter: FreeDatas2HTML;
let sortingFields: SortingFieldsStandAlone;
beforeEach( async () =>
{
document.body.insertAdjacentHTML("afterbegin", fixtures.datasViewEltHTML);
converter=new FreeDatas2HTML("CSV");
converter.parser.setRemoteSource({ url:"http://localhost:9876/datas/datas1.csv" });
converter.datasViewElt={ id:"datas" };
await converter.run(); // parsage + 1er affichage des données
});
afterEach( () =>
{
document.body.removeChild(document.getElementById("fixture"));
});
describe("Test des données reçues pour configurer les liens de classement.", () =>
{
it("Doit générer une erreur, si initialisé avant que les champs de données ne soient connus.", () =>
{
converter=new FreeDatas2HTML("CSV");
// Pas lancé converter.run(), donc les données n'ont pas été parsées :
expect(() => { return new SortingFieldsStandAlone(converter, { id:"selector1" }); }).toThrowError(errors.sortingFieldNeedDatas);
});
it("Doit générer une erreur, si initialisé avec l'id d'un élément n'existant pas dans le DOM", () =>
{
expect(() => { return new SortingFieldsStandAlone(converter, { id:"dontExist" }); }).toThrowError(errors.converterElementNotFound+"dontExist");
});
it("Si tous les paramètres sont ok, ils doivent être acceptés.", () =>
{
expect(() => { sortingFields=new SortingFieldsStandAlone(converter, { id:"selector1" }); }).not.toThrowError();
expect(sortingFields.converter).toEqual(converter);
expect(sortingFields.datasViewElt.id).toEqual("selector1");
});
});
describe("Création et action des liens de classement", () =>
{
let sortingField1: SortingField, sortingField2: SortingField;
beforeEach(() =>
{
sortingFields=new SortingFieldsStandAlone(converter, { id:"selector1" });
sortingField1=new SortingField(converter, 0);
sortingField2=new SortingField(converter, 2);
sortingFields.datasSortingFields=[sortingField1,sortingField2];
});
it("Doit générer une erreur, si lancé avant d'avoir fourni au moins un champ de classement.", () =>
{
sortingFields=new SortingFieldsStandAlone(converter, { id:"selector1" });
expect(() => { return sortingFields.rend2HTML() }).toThrowError(errors.needSortingFields);
});
it("Doit générer une liste de liens correspondant aux champs fournis .", () =>
{
sortingFields.rend2HTML();
let expectedHTML="<span></span><ul><li>"+fixtures.sortingColumn1HTML+"</li><li>"+fixtures.sortingColumn2HTML+"</li></ul>";
expect(document.getElementById("selector1").innerHTML).toEqual(expectedHTML);
// Idem avec des paramètres d'affichage :
sortingFields.rendSettings=
{
allBeginning: "<h4>#LABEL</h4><p>",
allEnding: "</p>",
fieldBeginning: "",
fieldEnding: " | ",
};
expectedHTML="<h4>Classer les données :</h4><p>"+fixtures.sortingColumn1HTML+" | "+fixtures.sortingColumn2HTML+" | </p>";
sortingFields.rend2HTML("Classer les données :");
expect(document.getElementById("selector1").innerHTML).toEqual(expectedHTML);
});
it("Lorsqu'ils sont cliqués, les liens de classement doivent transmettre l'information au convertisseur + lui demander d'actualiser l'affichage.", () =>
{
sortingFields.rend2HTML();
let getLinks=document.querySelectorAll("ul li a") as NodeListOf<HTMLElement>;
spyOn(converter, "refreshView");
getLinks[0].click();// tri ascendant 1er champ
expect(sortingField1.converter.datasSortedField).toEqual(sortingField1);
expect(sortingField1.converter.datasSortedField.order).toEqual("asc");
expect(converter.refreshView).toHaveBeenCalledTimes(1);
getLinks[1].click();// tri ascendant mais sur le second champ
expect(sortingField2.converter.datasSortedField).toEqual(sortingField2);
expect(sortingField2.converter.datasSortedField.order).toEqual("asc");
expect(converter.refreshView).toHaveBeenCalledTimes(2);
getLinks[0].click();// tri descendant sur le 1er champ
expect(sortingField1.converter.datasSortedField).toEqual(sortingField1);
expect(sortingField1.converter.datasSortedField.order).toEqual("desc");
expect(converter.refreshView).toHaveBeenCalledTimes(3);
getLinks[0].click();// de nouveau ascendant
expect(sortingField1.converter.datasSortedField).toEqual(sortingField1);
expect(sortingField1.converter.datasSortedField.order).toEqual("asc");
expect(converter.refreshView).toHaveBeenCalledTimes(4);
});
});
});
Loading…
Cancel
Save