[اختبار A/B] تغيير فئة CSS الأصل بناءً على متغير التجربة

لدينا سمة حالية نود تجربةها. على سبيل المثال، تجربة أنماط مختلفة لعرض قائمة المواضيع. نستخدم لهذا الغرض اختبار A/B عبر Google Optimize. حالياً، نخطط لعرض السمة دون تغييرات على 50% من المستخدمين، بينما تعرض النسخة المحدثة على الباقين. يجب تعريف النسخة الأصلية والنسخة البديلة ضمن نفس السمة لأننا سنجرّب جزءاً صغيراً فقط. المشكلة هي أن فئات CSS الأصلية تتشارك نفس الاسم، ونريد تطبيق أنماط على هذه الفئات الأصلية بناءً على متغير محدد. كيف يمكن تطبيق أنماط منفصلة على قوالب مختلفة وهي تتشارك نفس اسم الفئة الأصلية؟

فيما يلي سيناريو مبسط لما نحاول تحقيقه:

لنفترض أن لدي سمة يتم فيها تعريف نمط قائمة المواضيع في ملف common/common.scss كالتالي:

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

وفي نسختي البديلة، أريد ضبطها كالتالي:

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

ملفات Handlebar التي يتم تجاوزها تبدو كالتالي:

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>

يُذكر اسم الفئة topic-list في مكون Discourse، لكن الهيكل بعد التمثيل يكون كالتالي:

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

أريد عرض أحد هذين الملفين بناءً على معرف المستخدم في ملف الرأس الخاص بي:
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());
    }
  },
});

شكراً لكم

لاحظ أن هذا قد يتسبب في حدوث خطأ غير مُعالَج للمستخدمين الذين لم يسجلوا الدخول. أنصحك بتغيير ذلك إلى

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

const userId = user.id;

وبعد أن انتهينا من ذلك، لنتابع سؤالك.

على عكس CSS، يدعم SCSS محدد الأب، لذا يمكنك استخدام ذلك.

بما أنك تريد تطبيق هذا على كل مستخدم آخر (id % 2)

يمكنك التحقق من ذلك عند تحميل التطبيق. إذن، شيء من هذا القبيل في الأعلى تماماً

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

if (!userId) return;

const testThemeUser = userId % 2 === 0;

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

بعد ذلك يمكنك استخدام testThemeUser في الشروط البرمجية (JS conditionals) واستخدام فئة CSS test-theme في أنماطك.

إذن، شيء من هذا القبيل.

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;
  }
  // SCSS الخاص بالموضوع الاختباري
  .test-theme & {
    .example-div {
      border: 2px solid black;
    }
    .another-example-div {
      font-size: 10px;
    }
  }
}

شكرًا على ردك @Johani
حاليًا، أحتاج فقط إلى تحديث فئة مكون محددة وهي topic-list، لذا أستخدم هذا الأسلوب

const useTestTheme = userId % 2 === 0

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