Type de questions : swipe
Lancement de l'activité :
C'est une question qui disposent de deux réponses communes avec un texte qui change pour chaque carte.
Idéalement, dans l'éditeur, on rentre une question, les deux réponses possibles, et des textes qui correspondent aux textes présents sur les cartes.
Données & intégration
Le composant card se trouvera dans src/app/pages/player/assessment/components
Le composant card aura pour entrées :
@Input ('responses') responses: Array[String];
@Input ('SwipCard') swipCard: SwipCard;
@Input('disabled') disabled: boolean;
et pour sortie :
@Output() onSelectAnswer = new EventEmitter<string>();
Les données de card sont :
selectedAnswer
Card est un objet qui contient un id, un texte, et une explication individuelle facultative.
Détail du composant card
On dipose dans le composant du texte pour créer la carte et de question pour accéder aux deux réponses.
Dans le HTML du composant, on créer une seule carte
On peut utiliser <ion-card>
avec <ion-card-header>
et <ion-card-title>
etc ...
+ d'informations sur https://ionicframework.com/docs/api/card
doc : https://ionicframework.com/docs/utilities/gestures
vidéo complémentaire sur l'implémentation d'une gesture swipe : https://www.youtube.com/watch?v=S57QWQGZf3I
Dans le script de card, il y aura :
- selectAnswer(answer);
- La création de la gesture
- Des méthodes utiles qui seront appelés dans les
onMove
&onEnd
de la gesture
Dans le Script du composant, on peut créer la gesture "swipe" Les gestures ont souvent trois méthodes qui sont :
OnStart
OnMove
OnEnd
Quelques pistes sur OnMove
:
Lorsque la gesture est déclenché et qu'on commence le swipe, il faut faire bouger la carte en fonction de notre curseur.
Utiliser transform
avec une propriété translateX
et une propriété rotate
qui dépendent toutes de la position du curseur (deltaX
).
Il faut également indiquer la réponse selon si on est à gauche ou à droite, accessible grâce à reponses
Peut se faire à l'aide d'une méthode qu'on définit plus loin.
setRep(positionDuCurseur)
: qui vérifie la position du curseur.
reponses[0]
)
et vice-versa
Quelques pistes sur OnEnd
:
Lorsque la gesture est finie (quand on relâche), il y a deux cas :
-Si la position du curseur est au delà de la limite, c'est à dire du côté le plus près du bord de l'écran
On sort la carte de l'écran, ce qui laisse apparaître la carte juste en dessous
Utiliser transform
avec propriété translateX
et rotate
.
Pour la propriété translateX, il faut sortir la carte de l'écran. Pour accéder à la largeur de l'écran, on peut injecter Platform
dans le composant card (le constructeur) puis faire this.plateform.width.
On peut ensuite appeler selectAnswer(answer)
.
-Si la position du curseur est avant la limite (vers le centre)
On doit remettre la carte dans sa position initiale (avant le début de la gesture).
Transition où on déplace progressivement la carte vers le centre (C'est le même transform que celui de onMove
, avec un - devant le translateX
)
On doit aussi remettre le bandeau réponse de la carte à "".
setRep(positionDuCurseur)
gesture.enable(true);
Tests unitaires de card
Tester les entrées et sorties :
@Input ('responses') responses: Array[String];
@Input ('SwipCard') swipCard: SwipCard;
-
@Input('disabled') disabled: boolean;
et pour sortie : @Output() onSelectAnswer = new EventEmitter<string>();
Dans le fichier spec :
TestBed .configureTestingModule({declarations: [CardComponent]}) fixture = TestBed.createComponent(CardComponent); comp = fixture.componentInstance;
let component = CardComponent
cardDe = fixture.debugElement.query(By.css('.card'))
cardEl = cardDe.nativeElement
responses r = ['Vrai', 'Faux']
SwipCard c = new SwipCard(id: 42, texte: ".......")
expectedCard = { responses: r, swipCard: c, disabled:'false'}
comp.swipCard = expectedCard
fixture.detectChanges()
-
Tester si le composant est créer :
it('should create', () => { expect(component).toBeTruthy(); });
-
Pour la méthode
setRep(positionCurseur)
-
positionCurseur.deltaX is undefined
error
-
positionCurseur.deltaX < limite gauche
expect(fixture.debugElement.nativeElement.querySelector('.swipCard ionCardTitle').textContent).toEqual(responses[0]));
-
positionCurseur.deltaX > limite gauche et < limite droit
expect(fixture.debugElement.nativeElement.querySelector('.swipCard ionCardTitle').textContent).toEqual("")),
-
positionCurseur.deltaX > limite droit
expect(fixture.debugElement.nativeElement.querySelector('.swipCard ionCardTitle').textContent.toEqual(responses[1]));
-
-
Pour la méthode
selectAnswer(answer)
-
!answer.instanceOf(String)
expect(error);
-
answer est contenu dans question.responses
it('should raise onSelectAnswer event when (swipe) && answer in question.responses (triggerEventHandler)', () => {
let selectedCard: SwipCard;
comp.onSelectAnswer.subscribe((swipCard: SwipCard) => selectedCard = swipCard);
cardDe.triggerEventHandler('swipe', null);
expect(selectedCard).toBe(expectedCard);
});
-
answer est contenu dans question.reponses
it('shouldn't raise onSelectAnswer event when (swipe) && answer not in question.responses (triggerEventHandler)', () => {
expect(error);
});
-
Détails du composant swipe
On a besoin de créer un composant swipe, qui affichera toutes les cartes les unes sur les autres, ainsi que le nombre de cartes dans la pile de droite, de gauche
Les entrées de swipe :
-
@Input ('question') question: SwipeQuestion;
SwipeQuestion extends Question.
La classe ajoute : - un boolean expGeneral qui vaut true si l'explication est général sinon false.
- un array de SwipCard
- Redéfini explanation pour le passer en paramètre facultatif.
Les sorties de swipe :
@Output() onEndActivity() = new EventEmitter<Array[String]>();
Les données de swipe :
cartesRestantes : Array[SwipCard]
cartesTriees : Array[SwipCard]
Dans le HTML de swipe :
- Boucle sur l'array de cartesRestantes -> à chaque tour on créer un composant card.
- Affichage du compteur de la pile de droite et de gauche (incrémenté lors de réception de l'event émis par card)
- bouton annuler
Dans le script de swipe :
- Méthode pour incrémenter les piles en fct de la réponse de l'utilisateur
- Méthode pour annuler (décrémenter la pile, réafficher la carte, reculer dans la boucle)
- OnEndActivty, qui envoie un évent au parent assessment pour dire que l'activité est finie, c'est à ce moment qu'on check toutes les réponses je pense. Cette méthode sera appelée lorsque le compteur de pile droite + gauche sera égal au nombre de cartes (longueur de l'array question.swipCards).
Tests unitaires pour le composant swipe
-
Test si il est créé
-
Test si le bouton undo est initialisé à disabled
-
Test si la fin de l'activité renvoie bien un évènement onEndActivity au parent assessment.
-
Test si explanation = "" lorsque expGeneral = false ET cartes[0].explanation != ""
-
Pour la méthode
Annuler()
-
swipCards.length === cartesRestantes.length
it('shouldn't undo', () => {
Annuler();
expect(error);
});
-
swipCards.length < cartesRestantes.length
it('shouldn't undo', () => {
Annuler();
expect(error);
});
-
swipCards.length > cartesRestantes.length
swipCards = [carte1, carte2, carte3]
cartesRestantes = [carte2, carte3]
cartesTriees = [carte1]
it('should undo', () => {
Annuler();
expect(cartesTriees.toEqual([]);
expect(cartesRestantes.toEqual([carte1, carte2, carte3]);
});
-
swipCards.length === cartesRestantes.length
Détails du composant assessment suite à l'ajout des deux composants
Dans l'html : on insère <swipe>
si le type de la question est swipe.
Lors de la réception de l'évent de swipe, il faut check les réponses.
Pour se faire, on peut avoir accès aux deux réponses possibles avec questions.responses.
Je propose que question.correctResponses soit un array contenant les bonnes réponses associés aux cartes dans l'ordre. Ainsi, la bonne réponse de la carte numéro 3 sera question.correctResponses[2].
Idées pour le feedback à l'issue de la réunion de Mardi 20/04 15h
Pour cette activité : Feedback immédiat au moment où on swipe
Avantages :
- On sait facilement si on s'est trompés, c'est interactif (peut être même faire vibrer le téléphone si on a faux)
- Pas de problèmes quant à l'affichage des réponses de l'utilisateur et des bonnes réponses
Inconvénients :
- Peut être difficile, graphiquement, de donner une explication sans "casser le rythme"
- Dans la notation, il faut avoir tout juste pour valider, or, si on a faux dès le début, peu d'intérêt de continuer
⤵
Solution : Si on fait une erreur, on replace la carte au fond de la pile, jusqu'à que l'utilisateur ait tout juste.