[A/B 测试] 根据实验变量更改父级 CSS 类

我们有一个现有主题,希望对其进行实验。例如,可以实验不同的主题列表视图样式。为此,我们使用 Google Optimize 进行 A/B 测试。目前计划向 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;
  }
}

被覆盖的 Handlebars 文件如下所示:

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>

我希望在头文件中根据用户 ID 渲染这些文件之一:
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");
}

然后,你可以在 JS 条件语句中使用 testThemeUser,并在样式中使用 test-theme CSS 类。

例如:

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,
})