[A/B Testing] Modifica della classe CSS genitore in base alla variabile dell'esperimento

Abbiamo un tema esistente che vorremmo sperimentare. Un esempio sarebbe provare diversi stili per la visualizzazione dell’elenco degli argomenti. Per questo stiamo utilizzando i test A/B di Google Optimize. Attualmente, prevediamo di mostrare il tema senza modifiche al 50% degli utenti e il resto con il tema aggiornato. L’originale e la variante dovrebbero essere definiti nello stesso tema, poiché stiamo sperimentando solo una piccola parte. Il problema è che le classi CSS genitore condividono lo stesso nome e vogliamo applicare stili a queste classi genitore in base a una variabile specifica. Come possiamo applicare stili separati a diversi template dato che condividono lo stesso nome di classe genitore?

Di seguito è riportato uno scenario semplificato di ciò che stiamo cercando di ottenere.

Immagina di avere un tema in cui lo stile dell’elenco degli argomenti è definito in questo modo in common/common.scss:

.topic-list {
  tbody {
    border: none;
  }
  tr {
    border: none;
  }
  .example-div {
    border: none;
  }
}

E nella variante, voglio impostarlo così:

.topic-list {
  tbody {
    display: block;
    tr {
      display: block;
    }
  }
  .example-div {
    border: 2px solid black;
  }
  .another-example-div { 
    font-size: 10px;
  }
}

I file handlebar che vengono sovrascritti sono simili a questo:

javascripts/discourse/templates/list/custom-topic-list-item.hbr

<div class='example-div'>
  Hello
</div>

javascripts/discourse/templates/list/variant-custom-topic-list-item.hbr

<div class='example-div'>
  Hello,
  <div class='another-example-div'>
    Nice to meet you
  </div>
</div>

Il nome della classe topic-list è menzionato nel componente Discourse, ma la struttura dopo il rendering è questa:

<table class="topic-list">
  <tr class="topic-list-item">
   <div class='example-div'>
      Hello
    </div>
  </tr>
</table>

Voglio renderizzare uno di questi file in base all’ID utente nel mio file intestazione
common/header.html

const { findRawTemplate } = require("discourse-common/lib/raw-templates");
const user_id = api.getCurrentUser().id
 
api.modifyClass('component:topic-list-item', {
  renderTopicListItem() {
    let template = findRawTemplate("list/custom-topic-list-item");
    if (user_id % 2 === 0)
       template = findRawTemplate("list/variant-custom-topic-list-item");
    if (template) {
      this.set("topicListItemContents", template(this).htmlSafe());
    }
  },
});

Grazie

Tieni presente che questo probabilmente causa un errore non gestito per gli utenti non autenticati. Consiglio di modificarlo in questo modo:

const user = api.getCurrentUser();
if (!user) return;

const userId = user.id;

Detto questo, torniamo alla tua domanda.

A differenza di CSS, SCSS supporta un selettore genitore, quindi puoi utilizzare questa funzionalità.

Dato che vuoi applicare questo stile a ogni altro utente (id % 2)

Puoi verificare questo condizione quando l’app viene caricata. Quindi, qualcosa del genere all’inizio:

const user = api.getCurrentUser();
const userId = user.id;

if (!userId) return;

const testThemeUser = userId % 2 === 0;

if (testThemeUser) {
  document.body.classList.add("test-theme");
}

Puoi quindi utilizzare testThemeUser nelle tue condizioni JavaScript e la classe CSS test-theme nei tuoi stili.

Quindi, qualcosa del genere.

JS

let template = findRawTemplate("list/custom-topic-list-item");
if (testThemeUser)
  template = findRawTemplate("list/variant-custom-topic-list-item");
if (template) {
  this.set("topicListItemContents", template(this).htmlSafe());
}

SCSS

.topic-list {
  tbody {
    border: none;
  }
  tr {
    border: none;
  }
  .example-div {
    border: none;
  }
  // stili del tema di test
  .test-theme & {
    .example-div {
      border: 2px solid black;
    }
    .another-example-div {
      font-size: 10px;
    }
  }
}

Grazie per la risposta @Johani
Al momento ho solo bisogno di aggiornare una specifica classe di componente, ovvero topic-list, quindi sto usando questo approccio

const useTestTheme = userId % 2 === 0

api.modifyClass('component:topic-list', {
    classNameBindings: ["test"],
    test: useTestTheme,
})