UX: Use d-table classes instead of legacy d-admin-table (#38048)

In https://github.com/discourse/discourse/pull/33531 we
introduced the d-table class as a generic alternative to
d-admin-table, meant to replace it. However we didn't
update
https://meta.discourse.org/t/creating-consistent-admin-interfaces/326780
properly, nor did we update all places in the code that used
the old CSS classes, so over time we've ended up with a mix of both.

This commit updates all admin tables to use the new d-table classes, and
removes the old d-admin-table classes, updating templates, CSS, and
system specs as necessary.

We will remove the admin_table.scss file in a followup PR, since
other plugins may be using these classes.
This commit is contained in:
Martin Brennan
2026-02-26 09:37:56 +10:00
committed by GitHub
parent 2b09c9f736
commit 2ff2389812
17 changed files with 109 additions and 111 deletions
@@ -95,13 +95,13 @@ export default class AdminPluginsListItem extends Component {
<div class="admin-plugins-list__name-with-badges">
{{#if this.pluginUrl}}
<a
class="d-admin-row__overview-name admin-plugins-list__name"
class="d-table__overview-name admin-plugins-list__name"
href={{this.pluginUrl}}
>
{{@plugin.nameTitleized}}
</a>
{{else}}
<div class="d-admin-row__overview-name admin-plugins-list__name">
<div class="d-table__overview-name admin-plugins-list__name">
{{@plugin.nameTitleized}}
</div>
{{/if}}
@@ -120,10 +120,10 @@ export default class AdminPluginsListItem extends Component {
@outletArgs={{lazyHash plugin=@plugin}}
/>
</div>
<div class="d-admin-row__overview-author admin-plugins-list__author">
<div class="d-table__overview-author admin-plugins-list__author">
{{@plugin.author}}
</div>
<div class="d-admin-row__overview-about admin-plugins-list__about">
<div class="d-table__overview-about admin-plugins-list__about">
{{@plugin.about}}
{{#if @plugin.linkUrl}}
<a
@@ -61,7 +61,7 @@ export default class AdminUserUpcomingChanges extends Component {
{{/if}}
</td>
<td class="d-table__cell">
<div class="d-admin-row__mobile-label">
<div class="d-table__mobile-label">
{{i18n "user.upcoming_changes.for_user.enabled"}}
</div>
@@ -74,7 +74,7 @@ export default class AdminUserUpcomingChanges extends Component {
</span>
</td>
<td class="d-table__cell">
<div class="d-admin-row__mobile-label">
<div class="d-table__mobile-label">
{{i18n "user.upcoming_changes.for_user.why"}}
</div>
@@ -106,8 +106,8 @@ export default class ThemeSiteSettings extends Component {
}}
>
<:content as |filteredSettings|>
<table class="d-admin-table admin-theme-site-settings">
<thead>
<table class="d-table admin-theme-site-settings">
<thead class="d-table__header">
<tr>
<th>{{i18n "admin.theme_site_settings.setting"}}</th>
<th>{{i18n "admin.theme_site_settings.default_value"}}</th>
@@ -117,7 +117,7 @@ export default class ThemeSiteSettings extends Component {
<tbody>
{{#each filteredSettings as |fs|}}
<tr
class="admin-theme-site-settings-row d-admin-row__content"
class="admin-theme-site-settings-row d-table__row"
data-setting-name={{fs.name}}
>
<td class="admin-theme-site-settings-row__setting">
@@ -97,8 +97,8 @@ export default class AutomationList extends Component {
</DPageSubheader>
{{#if @model.content.length}}
<table class="d-admin-table automations">
<thead>
<table class="d-table automations">
<thead class="d-table__header">
<tr>
<th>{{i18n
"discourse_automation.models.automation.name.label"
@@ -120,13 +120,13 @@ export default class AutomationList extends Component {
</thead>
<tbody>
{{#each @model.content as |automation|}}
<tr class="d-admin-row__content">
<tr class="d-table__row">
{{#if automation.script.not_found}}
<td
colspan="5"
class="d-admin-row__detail alert alert-danger"
class="d-table__cell --detail alert alert-danger"
>
<div class="d-admin-row__mobile-label">
<div class="d-table__mobile-label">
{{i18n
"discourse_automation.models.automation.status.label"
}}
@@ -140,9 +140,9 @@ export default class AutomationList extends Component {
{{else if automation.trigger.not_found}}
<td
colspan="5"
class="d-admin-row__detail alert alert-danger"
class="d-table__cell --detail alert alert-danger"
>
<div class="d-admin-row__mobile-label">
<div class="d-table__mobile-label">
{{i18n
"discourse_automation.models.automation.status.label"
}}
@@ -154,15 +154,15 @@ export default class AutomationList extends Component {
}}
</td>
{{else}}
<td class="d-admin-row__overview automations__name">
<td class="d-table__cell --overview automations__name">
{{if
automation.name
automation.name
(i18n "discourse_automation.unnamed_automation")
}}
</td>
<td class="d-admin-row__detail automations__updated-by">
<div class="d-admin-row__mobile-label">
<td class="d-table__cell --detail automations__updated-by">
<div class="d-table__mobile-label">
{{i18n
"discourse_automation.models.automation.last_updated_by.label"
}}
@@ -177,8 +177,8 @@ export default class AutomationList extends Component {
{{formatDate automation.updated_at leaveAgo="true"}}
</div>
</td>
<td class="d-admin-row__detail automations__runs">
<div class="d-admin-row__mobile-label">
<td class="d-table__cell --detail automations__runs">
<div class="d-table__mobile-label">
{{i18n
"discourse_automation.models.automation.runs.label"
}}
@@ -199,8 +199,8 @@ export default class AutomationList extends Component {
</a>
{{/if}}
</td>
<td class="d-admin-row__detail automations__last-run">
<div class="d-admin-row__mobile-label">
<td class="d-table__cell --detail automations__last-run">
<div class="d-table__mobile-label">
{{i18n
"discourse_automation.models.automation.last_run.label"
}}
@@ -214,8 +214,8 @@ export default class AutomationList extends Component {
-
{{/if}}
</td>
<td class="d-admin-row__detail automations__enabled">
<div class="d-admin-row__mobile-label">
<td class="d-table__cell --detail automations__enabled">
<div class="d-table__mobile-label">
{{i18n
"discourse_automation.models.automation.enabled.label"
}}
@@ -233,7 +233,7 @@ export default class AutomationList extends Component {
</td>
{{/if}}
<td class="d-admin-row__controls automations__controls">
<td class="d-table__cell --controls automations__controls">
<LinkTo
@route="adminPlugins.show.automation.edit"
@model={{automation.id}}
@@ -29,8 +29,8 @@
margin-top: 1em;
}
.d-admin-table.automations {
.d-admin-row__content {
.d-table.automations {
.d-table__row {
@include viewport.until(md) {
display: grid;
grid-template-columns: 1fr;
@@ -38,31 +38,31 @@
}
}
.d-admin-row__name {
.automations__name {
@include viewport.until(md) {
order: 1;
}
}
.d-admin-row__detail.automations__runs {
.d-table__cell.--detail.automations__runs {
@include viewport.until(md) {
order: 2; // move below the name to avoid empty spacing
}
}
.d-admin-row__detail.automations__updated-by {
.d-table__cell.--detail.automations__updated-by {
@include viewport.until(md) {
order: 3;
}
}
.d-admin-row__detail.automations__enabled {
.d-table__cell.--detail.automations__enabled {
@include viewport.until(md) {
order: 5;
}
}
.d-admin-row__controls.automations__controls {
.d-table__cell.--controls.automations__controls {
text-align: right;
display: flex;
flex-direction: row;
@@ -49,8 +49,8 @@ export default class AdminChatIncomingWebhooksList extends Component {
}
<template>
<table class="d-admin-table">
<thead>
<table class="d-table">
<thead class="d-table__header">
<th>{{i18n "chat.incoming_webhooks.name"}}</th>
<th>{{i18n "chat.incoming_webhooks.emoji"}}</th>
<th>{{i18n "chat.incoming_webhooks.username"}}</th>
@@ -61,39 +61,39 @@ export default class AdminChatIncomingWebhooksList extends Component {
<tbody>
{{#each this.sortedWebhooks as |webhook|}}
<tr
class="d-admin-row__content incoming-chat-webhooks-row"
class="d-table__row incoming-chat-webhooks-row"
data-webhook-id={{webhook.id}}
>
<td class="d-admin-row__overview">
<div class="d-admin-row__overview-name">
<td class="d-table__cell --overview">
<div class="d-table__overview-name">
{{webhook.name}}
</div>
<div class="d-admin-row__overview-about">
<div class="d-table__overview-about">
{{webhook.description}}
</div>
</td>
<td class="d-admin-row__detail">
<div class="d-admin-row__mobile-label">
<td class="d-table__cell --detail">
<div class="d-table__mobile-label">
{{i18n "chat.incoming_webhooks.emoji"}}
</div>
{{replaceEmoji webhook.emoji}}
</td>
<td class="d-admin-row__detail">
<div class="d-admin-row__mobile-label">
<td class="d-table__cell --detail">
<div class="d-table__mobile-label">
{{i18n "chat.incoming_webhooks.username"}}
</div>
{{webhook.username}}
</td>
<td class="d-admin-row__detail">
<div class="d-admin-row__mobile-label">
<td class="d-table__cell --detail">
<div class="d-table__mobile-label">
{{i18n "chat.incoming_webhooks.channel"}}
</div>
<ChannelTitle @channel={{webhook.chat_channel}} />
</td>
<td
class="d-admin-row__controls incoming-chat-webhooks-row__controls"
class="d-table__cell --controls incoming-chat-webhooks-row__controls"
>
<div class="d-admin-row__controls-options">
<div class="d-table__cell-actions">
<LinkTo
@route="adminPlugins.show.discourse-chat-incoming-webhooks.edit"
@model={{webhook.id}}
@@ -2,8 +2,8 @@ import { LinkTo } from "@ember/routing";
import { i18n } from "discourse-i18n";
const HouseAdsList = <template>
<table class="d-admin-table house-ads-table" ...attributes>
<thead>
<table class="d-table house-ads-table" ...attributes>
<thead class="d-table__header">
<tr>
<th>{{i18n "admin.adplugin.house_ads.name"}}</th>
<th></th>
@@ -11,11 +11,11 @@ const HouseAdsList = <template>
</thead>
<tbody>
{{#each @houseAds as |ad|}}
<tr class="d-admin-row__content" data-house-ad-id={{ad.id}}>
<td class="d-admin-row__overview">
<tr class="d-table__row" data-house-ad-id={{ad.id}}>
<td class="d-table__cell --overview">
{{ad.name}}
</td>
<td class="d-admin-row__controls">
<td class="d-table__cell --controls">
<LinkTo
@route="adminPlugins.show.houseAds.show"
@model={{ad.id}}
@@ -24,18 +24,16 @@ module PageObjects
end
def click_ad(name)
find(".house-ads-table tr.d-admin-row__content", text: name).find(
".house-ads-table__edit",
).click
find(".house-ads-table tr.d-table__row", text: name).find(".house-ads-table__edit").click
self
end
def has_ad_listed?(name)
has_css?(".house-ads-table tr.d-admin-row__content", text: name)
has_css?(".house-ads-table tr.d-table__row", text: name)
end
def has_no_ad_listed?(name)
has_no_css?(".house-ads-table tr.d-admin-row__content", text: name)
has_no_css?(".house-ads-table tr.d-table__row", text: name)
end
def click_delete
@@ -44,8 +44,8 @@ export default class AiEmbeddingsListEditor extends Component {
</DPageSubheader>
{{#if this.hasEmbeddingElements}}
<table class="d-admin-table">
<thead>
<table class="d-table">
<thead class="d-table__header">
<tr>
<th>{{i18n "discourse_ai.embeddings.display_name"}}</th>
<th>{{i18n "discourse_ai.embeddings.provider"}}</th>
@@ -54,16 +54,16 @@ export default class AiEmbeddingsListEditor extends Component {
</thead>
<tbody>
{{#each @embeddings.content as |embedding|}}
<tr class="ai-embeddings-list__row d-admin-row__content">
<td class="d-admin-row__overview">
<tr class="ai-embeddings-list__row d-table__row">
<td class="d-table__cell --overview">
<div class="ai-embeddings-list__name">
<strong>
{{embedding.display_name}}
</strong>
</div>
</td>
<td class="d-admin-row__detail">
<div class="d-admin-row__mobile-label">
<td class="d-table__cell --detail">
<div class="d-table__mobile-label">
{{i18n "discourse_ai.embeddings.provider"}}
</div>
{{i18n
@@ -72,7 +72,7 @@ export default class AiEmbeddingsListEditor extends Component {
)
}}
</td>
<td class="d-admin-row__controls">
<td class="d-table__cell --controls">
{{#if embedding.seeded}}
<DTooltip
class="ai-embeddings-list__edit-disabled-tooltip"
@@ -160,8 +160,8 @@ export default class AiLlmsListEditor extends Component {
<DPageSubheader
@titleLabel={{i18n "discourse_ai.llms.configured.title"}}
/>
<table class="d-admin-table">
<thead>
<table class="d-table">
<thead class="d-table__header">
<tr>
<th>{{i18n "discourse_ai.llms.display_name"}}</th>
<th>{{i18n "discourse_ai.llms.provider"}}</th>
@@ -172,9 +172,9 @@ export default class AiLlmsListEditor extends Component {
{{#each @llms.content as |llm|}}
<tr
data-llm-id={{llm.name}}
class="ai-llm-list__row d-admin-row__content"
class="ai-llm-list__row d-table__row"
>
<td class="d-admin-row__overview">
<td class="d-table__cell --overview">
<div class="ai-llm-list__name">
<strong>
@@ -225,15 +225,15 @@ export default class AiLlmsListEditor extends Component {
</div>
{{/if}}
</td>
<td class="d-admin-row__detail">
<div class="d-admin-row__mobile-label">
<td class="d-table__cell --detail">
<div class="d-table__mobile-label">
{{i18n "discourse_ai.llms.provider"}}
</div>
{{i18n
(concat "discourse_ai.llms.providers." llm.provider)
}}
</td>
<td class="d-admin-row__controls">
<td class="d-table__cell --controls">
<DButton
class="btn btn-default btn-small ai-llm-list__edit-button"
@label="discourse_ai.llms.edit"
@@ -263,11 +263,11 @@ export default class AiPersonaListEditor extends Component {
<:content as |filteredPersonas|>
<table
class={{concatClass
"content-list ai-persona-list-editor d-admin-table"
"content-list ai-persona-list-editor d-table"
(concat "--layout-" this.currentLayout.id)
}}
>
<thead>
<thead class="d-table__header">
<tr>
<th>{{i18n "discourse_ai.ai_persona.name"}}</th>
<th>{{i18n "discourse_ai.llms.short_title"}}</th>
@@ -279,12 +279,12 @@ export default class AiPersonaListEditor extends Component {
<tr
data-persona-id={{persona.id}}
class={{concatClass
"ai-persona-list__row d-admin-row__content"
"ai-persona-list__row d-table__row"
(if persona.priority "--priority")
(if persona.enabled "--enabled")
}}
>
<td class="d-admin-row__overview">
<td class="d-table__cell --overview">
<div class="ai-persona-list__name-with-description">
<h3 class="ai-persona-list__name">
{{#if persona.user}}
@@ -297,7 +297,7 @@ export default class AiPersonaListEditor extends Component {
</div>
</div>
</td>
<td class="d-admin-row__llms">
<td class="ai-persona-list__llms">
{{#if persona.default_llm}}
<span class="--card-label">
{{i18n "discourse_ai.ai_persona.llms_list"}}
@@ -310,7 +310,7 @@ export default class AiPersonaListEditor extends Component {
/>
{{/if}}
</td>
<td class="d-admin-row__features">
<td class="ai-persona-list__features">
{{#if persona.features.length}}
<span class="--card-label">
{{i18n
@@ -319,7 +319,7 @@ export default class AiPersonaListEditor extends Component {
}}
</span>
{{#each persona.features as |feature index|}}
<span class="d-admin-row__row-feature-list">
<span class="ai-persona-list__feature-list">
{{#if (gt index 0)}}, {{/if}}
<DButton
class="btn-flat btn-small ai-persona-list__row-item-feature"
@@ -337,7 +337,7 @@ export default class AiPersonaListEditor extends Component {
{{/each}}
{{/if}}
</td>
<td class="d-admin-row__controls">
<td class="d-table__cell --controls">
<LinkTo
@route="adminPlugins.show.discourse-ai-personas.edit"
@model={{persona}}
@@ -40,8 +40,8 @@ export default class AiSecretsListEditor extends Component {
</DPageSubheader>
{{#if this.hasSecrets}}
<table class="d-admin-table ai-secret-list-editor__table">
<thead>
<table class="d-table ai-secret-list-editor__table">
<thead class="d-table__header">
<tr>
<th>{{i18n "discourse_ai.secrets.name"}}</th>
<th>{{i18n "discourse_ai.secrets.used_by"}}</th>
@@ -52,13 +52,13 @@ export default class AiSecretsListEditor extends Component {
{{#each @secrets.content as |secret|}}
<tr
data-secret-id={{secret.id}}
class="ai-secret-list__row d-admin-row__content"
class="ai-secret-list__row d-table__row"
>
<td class="d-admin-row__overview">
<td class="d-table__cell --overview">
<strong>{{secret.name}}</strong>
</td>
<td class="d-admin-row__detail ai-secret-list__usage">
<div class="d-admin-row__mobile-label">
<td class="d-table__cell --detail ai-secret-list__usage">
<div class="d-table__mobile-label">
{{i18n "discourse_ai.secrets.used_by"}}
</div>
{{#if secret.used_by}}
@@ -102,7 +102,7 @@ export default class AiSecretsListEditor extends Component {
</span>
{{/if}}
</td>
<td class="d-admin-row__controls">
<td class="d-table__cell --controls">
<DButton
class="btn btn-default btn-small ai-secret-list__edit-button"
@label="discourse_ai.secrets.edit"
@@ -270,8 +270,8 @@ export default class AiToolListEditor extends Component {
</DPageSubheader>
{{#if @tools.content}}
<table class="d-admin-table ai-tool-list-editor">
<thead>
<table class="d-table ai-tool-list-editor">
<thead class="d-table__header">
<th>{{i18n "discourse_ai.tools.name"}}</th>
<th></th>
</thead>
@@ -279,9 +279,9 @@ export default class AiToolListEditor extends Component {
{{#each @tools.content as |tool|}}
<tr
data-tool-id={{tool.id}}
class="ai-tool-list__row d-admin-row__content"
class="ai-tool-list__row d-table__row"
>
<td class="d-admin-row__overview">
<td class="d-table__cell --overview">
<div class="ai-tool-list__name-with-description">
<div class="ai-tool-list__name">
<strong>
@@ -319,7 +319,7 @@ export default class AiToolListEditor extends Component {
{{/if}}
</div>
</td>
<td class="d-admin-row__controls">
<td class="d-table__cell --controls">
<LinkTo
@route="adminPlugins.show.discourse-ai-tools.edit"
@model={{tool}}
@@ -10,8 +10,8 @@
border: none;
padding: 0;
&.d-admin-row__llms,
&.d-admin-row__features {
&.ai-persona-list__llms,
&.ai-persona-list__features {
padding-block: 0;
.--card-label {
@@ -93,7 +93,7 @@
display: none;
}
.d-admin-row__content {
.d-table__row {
display: grid;
grid-template-rows: auto 1fr auto auto;
grid-template-columns: 1fr auto;
@@ -101,7 +101,7 @@
padding: var(--space-2) var(--space-4) var(--space-4);
border-radius: var(--d-border-radius);
.d-admin-row__overview,
.d-table__cell.--overview,
.ai-persona-list__name-with-description {
display: contents;
}
@@ -126,12 +126,12 @@
margin: var(--space-2) 0;
}
.d-admin-row__controls {
.d-table__cell.--controls {
grid-row: 1;
grid-column: 2;
}
.d-admin-row__features {
.ai-persona-list__features {
grid-row: 4;
grid-column: 1 / span 2;
padding: 0;
@@ -141,7 +141,7 @@
}
}
.d-admin-row__llms {
.ai-persona-list__llms {
grid-row: 3;
grid-column: 1 / span 2;
padding: 0;
@@ -191,7 +191,7 @@
}
}
.d-admin-row__row-feature-list {
.ai-persona-list__feature-list {
color: var(--primary-medium);
}
}
@@ -49,7 +49,7 @@
.ai-tool-list-editor__current,
.ai-persona-list-editor__current,
.ai-llms-list-editor__configured {
.d-admin-table {
.d-table {
@include viewport.from(md) {
th,
td {
@@ -88,8 +88,8 @@ export default <template>
<ConditionalLoadingSpinner @condition={{@controller.loading}} />
<div class="container discourse-data-explorer-query-list">
<table class="d-admin-table recent-queries">
<thead class="heading-container">
<table class="d-table recent-queries">
<thead class="d-table__header heading-container">
<th class="col heading name">
<div
role="button"
@@ -149,8 +149,8 @@ export default <template>
</thead>
<tbody>
{{#each filteredQueries as |query|}}
<tr class="d-admin-row__content query-row">
<td class="d-admin-row__overview">
<tr class="d-table__row query-row">
<td class="d-table__cell --overview">
<a
class="query-link"
href={{getURL
@@ -169,8 +169,8 @@ export default <template>
<div class="query-desc">{{query.description}}</div>
</a>
</td>
<td class="d-admin-row__detail query-created-by">
<div class="d-admin-row__mobile-label">
<td class="d-table__cell --detail query-created-by">
<div class="d-table__mobile-label">
{{i18n "explorer.query_user"}}
</div>
{{#if query.username}}
@@ -181,8 +181,8 @@ export default <template>
</div>
{{/if}}
</td>
<td class="d-admin-row__detail query-group-names">
<div class="d-admin-row__mobile-label">
<td class="d-table__cell --detail query-group-names">
<div class="d-table__mobile-label">
{{i18n "explorer.query_groups"}}
</div>
<div class="group-names">
@@ -194,8 +194,8 @@ export default <template>
{{/unless}}
</div>
</td>
<td class="d-admin-row__detail query-created-at">
<div class="d-admin-row__mobile-label">
<td class="d-table__cell --detail query-created-at">
<div class="d-table__mobile-label">
{{i18n "explorer.query_time"}}
</div>
{{#if query.last_run_at}}
@@ -32,7 +32,7 @@ module PageObjects
def setting_row(setting_name)
page.find(
".d-admin-row__content.admin-theme-site-settings-row[data-setting-name='#{setting_name}']",
".d-table__row.admin-theme-site-settings-row[data-setting-name='#{setting_name}']",
)
end
end