fix(widgets): restore the 20px bottom gap under the quote and search widgets

The reference wraps these two widgets in a middle .theme-box layer
(widget-container > .theme-box > title + card), which is what supplies the
20px bottom margin. Our outer .jio-widget-box-wrap is only a transparent
layout container, so dropping .theme-box left the quote card flush against
the following module.

- search/quote: emit the reference .theme-box middle layer; the card itself
  stays margin-free like the reference, so the gap is exactly 20px
- keep the generic .widget-content branch for user_card/author_card and the
  other reference widgets untouched (the string is contract-tested)
- contract test now pins the .theme-box layer and asserts the quote card
  carries no margin class of its own

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kate522andClaude Opus 5 committed 2026-11-03 16:09:46 +08:00
1 parent f80ca1e27d
commit 3c07ef7a8d
3 files changed
+15 -9

No files matched your search

+9 -5
View File
@@ -124,13 +124,17 @@ final class FrontendRenderer {
$style = self::style($instance);
$affix_attr = !empty($instance['sidebar_affix']) ? ' data-affix="true"' : '';
echo '<div class="' . esc_attr(implode(' ', $classes)) . '" data-jio-widget="' . esc_attr($definition->slug()) . '" data-widget-id="' . esc_attr((string) $widget->id) . '"' . $affix_attr . ($style !== '' ? ' style="' . esc_attr($style) . '"' : '') . '>';
if ($own_card) {
// 参考主题:搜索框/一言的中间层是 .theme-box,承载 20px 底部间距与入场动画。
echo '<div class="widget-container"><div class="theme-box' . esc_attr($animation) . '">';
echo self::reference_title($instance); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '</div></div></div>';
return;
}
echo '<div class="widget-container"><div>';
echo self::reference_title($instance); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
if ($own_card) {
echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} else {
echo '<div class="widget-content' . esc_attr($animation) . '">' . $content . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
echo '<div class="widget-content' . esc_attr($animation) . '">' . $content . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '</div></div></div>';
}
@@ -55,6 +55,7 @@ final class GeneralRenderer {
return '';
}
// 参考主题:.yiyan-box 既是卡片也是点击切换的根元素,内部只有 .yiyan。
// 20px 底部间距由外层 .theme-box 提供(与参考主题一致),卡片自身不带边距。
return jio_get_yiyan_markup([
'class' => 'yiyan-box main-bg text-center box-body radius8 main-shadow',
]);
@@ -18,16 +18,17 @@ test('搜索框与一言小工具只渲染参考主题的一张卡片', () => {
assert.doesNotMatch(rendered.search, /jio-widget-shell/);
assert.doesNotMatch(rendered.quote, /jio-widget-shell/);
// 搜索框:widget-container > 标题 + 单张 .widget-search 卡片,且复用共享搜索组件。
// 搜索框:widget-container > .theme-box(承载 20px 底距)+ 标题 + 单张卡片。
assert.equal((rendered.search.match(/class="jio-panel/g) || []).length, 1);
assert.match(rendered.search, /<div class="widget-container"><div><div class="box-body notop"><div class="title-theme">/);
assert.match(rendered.search, /<div class="widget-container"><div class="theme-box"><div class="box-body notop"><div class="title-theme">/);
assert.match(rendered.search, /class="jio-panel widget-search"><div class="search-input">/);
assert.equal((rendered.search.match(/class="search-input"/g) || []).length, 1);
// 一言:widget-container > 标题 + 单张 .yiyan-box 卡片(点击根元素即卡片)。
// 一言:同样的 .theme-box 间距层,卡片自身不带底部边距。
assert.equal((rendered.quote.match(/class="yiyan-box/g) || []).length, 1);
assert.match(rendered.quote, /<div class="widget-container"><div><div class="box-body notop"><div class="title-theme">/);
assert.match(rendered.quote, /<div class="widget-container"><div class="theme-box"><div class="box-body notop"><div class="title-theme">/);
assert.match(rendered.quote, /class="yiyan-box main-bg text-center box-body radius8 main-shadow jio-yiyan" data-jio-yiyan>/);
assert.doesNotMatch(rendered.quote, /class="yiyan-box[^"]*theme-box/);
assert.match(rendered.quote, /<div class="yiyan"><\/div>/);
});