14 novembre 2015

React : interface monopage avec timeline

Quelques jours passés sur une interface de type SPA, que j'ai conçue et développée en vue de connaître ReactJS.

Elle interroge l'API de Github et a pour objectif de rechercher des dépôts. Elle va permettre de les trouver via un terme présent dans leur nom ; l'utilisateur est en option.

"Recherche de dépôts sur Github"

C'est une interface monopage qui présente un formulaire de recherche, la liste des résultats, et une information détaillée pour chacun des résultats.
Toujours présent, le formulaire établit la première vue, avec ou sans résultats :

Les résultats affichent (pour l'instant) les 50 premiers.

Ce travail a principalement consisté à concevoir et à développer les fonctionnalités de l'interface - sans perfectionner ni négliger, toutefois, l'habillage graphique.
Notons au passage que le code généré via React passe le validateur du W3C.

L'information détaillée

Pour chaque résultat de la recherche initiale, une information détaillée est proposée. C'est la seconde vue de l'interface, qui se superpose à la première. Elle déroule les listes des contributeurs et des derniers commits - ces deux listes sont limitées à 100 (1).
La recherche détaillée est aussi illustrée d'une timeline, github:interfacteur/ interfacteur/github-api.

L'accès aux détails du résultat précédent et à ceux du résultat suivant sont proposés.

Liens vers les résultats suivant et précédent

De retour aux résultats initiaux, celui que l'on vient de consulter est distingué dans la liste.

Le résultat que l'on vient de consulter est distingué des autres

Une timeline de qualité

La timeline montre l'évolution chronologique des commits du dépôt. La mention du nombre de commits est portée, à la base, pour les principaux contributeurs.

La Timeline

Cette timeline est composée d'un tableau de données <>. Sémantique et CSS lui donnent sens et apparence.

Par estimation, j'ai adopté une découpe en 50 tranches chronologiques, quelle que soit la durée séparant premier et dernier commits - quelques exemples au 14/11/2015 :

Sauf pour un commit solitaire, githubapi/mcrouter/didip/mcrouter-util

Dans le sens d'une datavisualisation, j'ai trouvé en React un niveau de granularité suffisant, les données traitées prenant forme dans une filiation quasi "sémantique" des composants.

Les conditions de navigation

  1. L'adresse et le titre varient en fonction des résultats affichés. Tout contenu est donc bookmarquable, comme on a pu le constater par les exemples précédents.
  2. La gestion constante du focus facilite des usages sans souris.
  3. Autre confort de navigation, la pression sur une combinaison clavier de nouvel onglet du navigateur, lors du clic sur un lien.
    Certaines propriétés 'props' sont de facto présentes dans le code de la page <> via l'attribut 'href', en-dehors de toute gestion <> événementielle.
    La modification de contenu dans l'onglet actif est alors neutralisée <> ; une ouverture d'onglet est effectivement enclenchée. Pas de mise à jour réactive par les composants, laisser l'attribut 'href' autrement ignoré <> fonctionner dans l'onglet ainsi ouvert, que le contenu visé découle de l'url.

Côté développement

Ces vues sont donc générées par React.

Un premier ensemble de composants <> produit le formulaire et la liste de résultats. Un deuxième ensemble <> produit les deux listes du mode détaillé ; et un troisième <> en produit la timeline.
Si le formulaire est généré <>, c'est afin de réagir au chemin ('pathname') de l'url, aussi bien lors de l'affichage de la page que lors d'une consultation via l'historique du navigateur.

L'adoption de quelques principes de programmation fonctionnelle rapproche le code de l'état d'esprit de React.

Question

La mutualisation de méthodes au niveau des composants parents nécessite de binder l'action du composant enfant pour en transmettre la référence d'objet 'this'. Cette référence ne paraissant pas modifiable dans un composant, elle est transmise à la méthode bindée comme argument <> par le deuxième argument de 'bind()'.

Dans l'exécution de la méthode, cet argument <> remplace 'this'.
Quels autres moyens développer pour cela ?

Côté serveur, un .htaccess <> traite l'url de la requête initiale, par nettoyage, re-écriture, re-direction invisible vers l'index :

Puis l'API history maintient une cohérence constante entre contenus et adresse :

  • l'url s'adaptant dès l'aboutissement de requêtes vers l'API <> ;
  • inversement, la requête de contenus pouvant être lancée <> par l'url lors du chargement initial - ou de l'utilisation des fonctions "précédent" et "suivant" du navigateur <> <>.

React prend en charge la couche vue sans assurer le routage. , et comme Browser (qui accompagne le pack React) est lourd,
Sur ce point, le recours aux fonctions de history, et le concours des expressions régulières <> et du .htaccess <>, limitent l'investissement en code et en frameworks - tout en demandant une concentration algorithmique fine <>.

Pas de difficulté particulière avec l'API de Github <> (sauf sur IE9, cf. plus bas la question du cross-domain) - édit : l'API est accessible via JSON-P <>.

L'on a à "mapper" plusieurs fois les données <> JSON re-affectées à des tableaux pour obtenir tous les classements désirés, ainsi de githubapi/mcrouter/facebook/mcrouter.

Du côté des erreurs : il faut aussi ne pas oublier de considérer le cas des dépôts vides <>, qui provoquent l'erreur 409. En JSON, c'est une erreur réseau. En JSON-P, elle est encapsulée dans la propriété 'meta.status' de l'objet retourné.
Autres erreurs à prévoir avec l'option utilisateur : l'utilisateur n'existe pas, ou bien il existe mais n'a pas de dépôt public. L'erreur 422 <> renvoyée est commune aux deux cas.
Exemples au 04/12/2015 :

Outils de compatibilité

L'application fonctionne sur Firefox, Chrome, Safari 5+, IE9+ (testé en vm).

Le support des fonctions de l'API history est assuré si nécessaire <> par History.js. Un patch <> sert à conserver la syntaxe standard de l'API dans les développements :

history = window.History;
for (var k in window.History)
 history[k] = window.History[k];
history.state = window.History.getState().data;
window.History.Adapter.bind(window, "statechange", function (e) {
 history.state = window.History.getState().data;
});

History.js se contente de compléter le support de history sur un navigateur tel que Safari 5.1.9. Sur IE9, elle le compense entièrement, avec une gestion des url <> par ancre.

En passant, voici quelques points connexes à IE9, testé en vm :

  • cross-domain non soluble par les solutions additionnelles (…) etc. d'où utilisation d'un proxy (*) par le script additionnel jQuery-ajaxTransport-XDomainRequest : mais finalement aisément gérable via JSON-P ;
  • le double load <> des scripts alternatifs ;
  • un bug (to do <>) : la recherche par utilisateur ne fonctionne pas.

(*) Note en passant : avec un proxy, le retour des données Github ne paraît pas identique à celui d'une requête directe.

Sur Safari 5, simuler un clic peut poser problème, ce que contourne <> jquery.simulate.js. Là aussi, un patch <> sert à l'uniformité de l'écriture.

Les CSS gradients de la timeline sont compensables <> par un fallback, après détection <>.

Malgré un souhait d'utiliser les promesses, la coordination des requêtes Ajax est faite simplement <> par la méthode jQuery.when().

À ce jour, pas eu l'opportunité de tester l'interface sur Windows - édit 29/11/2015 : autrement que sur machine virtuelle.

Ce développement (< 1000 l. js) est déposé sur Github.

Aucun commentaire: