继续收口首页可导航扁平行
新增 PlatformNavigableListItem 统一 desktop flat row 的交互骨架 接入首页搜索结果与桌面最近作品最近浏览入口 补充组件测试首页回归测试并更新收口计划和共享决策记录
This commit is contained in:
@@ -55,6 +55,7 @@
|
|||||||
- 2026-06-10 追加:`CustomWorldEntityCatalog` 的 `minimum-playable` 规则阻断提示也统一迁到 `PlatformStatusDialog`,不再和删除角色 / 批量删除共用 `UnifiedConfirmDialog` 配置;同日平台入口公开编号搜索把 error 分支从用户摘要 modal 中拆出,未命中结果单独走 `PlatformStatusDialog`,命中用户继续保留 `UnifiedModal + PlatformSubpanel` 信息布局。
|
- 2026-06-10 追加:`CustomWorldEntityCatalog` 的 `minimum-playable` 规则阻断提示也统一迁到 `PlatformStatusDialog`,不再和删除角色 / 批量删除共用 `UnifiedConfirmDialog` 配置;同日平台入口公开编号搜索把 error 分支从用户摘要 modal 中拆出,未命中结果单独走 `PlatformStatusDialog`,命中用户继续保留 `UnifiedModal + PlatformSubpanel` 信息布局。
|
||||||
- 2026-06-11 追加:`PlatformAsyncStatePanel` 继续从 profile modal 与作品架扩展到 RPG 首页公开分区;`RpgEntryHomeView.tsx` 的移动端排行、发现页寓教于乐 / 默认公开 feed、桌面首页“今日游戏 / 推荐”、桌面发现页寓教于乐 / 默认公开 feed,以及“我的创作”分区已统一改成 `loadingState / emptyState / children` 三态 slot。页面级 `platformError` 继续留在状态壳外层,保证错误提示可以和内容并存;`recommend runtime`、分类筛选等含运行态或二级筛选语义的分支暂不硬并入这一轮。
|
- 2026-06-11 追加:`PlatformAsyncStatePanel` 继续从 profile modal 与作品架扩展到 RPG 首页公开分区;`RpgEntryHomeView.tsx` 的移动端排行、发现页寓教于乐 / 默认公开 feed、桌面首页“今日游戏 / 推荐”、桌面发现页寓教于乐 / 默认公开 feed,以及“我的创作”分区已统一改成 `loadingState / emptyState / children` 三态 slot。页面级 `platformError` 继续留在状态壳外层,保证错误提示可以和内容并存;`recommend runtime`、分类筛选等含运行态或二级筛选语义的分支暂不硬并入这一轮。
|
||||||
- 2026-06-11 追加:暗色 / 像素 modal 的标准 footer 布局统一抽到 `src/components/common/PlatformDarkModalFooter.tsx`;该组件只负责 dark footer 的分隔线、padding 和常见动作区排布,不持有“取消 / 确认”业务语义。`NpcModals.tsx` 的交易 / 赠礼 / 招募 footer、`SelectionCustomizationModals.tsx` 的 `SelectionModal` footer、`RpgAdventurePanelOverlays.tsx` 的 goal panel footer,以及 `InventoryItemViews.tsx` 的详情 footer wrapper 已接入;sticky 工作台 footer、正文内单 CTA 收尾和 runtime HUD 工具条暂不并入这一抽象。
|
- 2026-06-11 追加:暗色 / 像素 modal 的标准 footer 布局统一抽到 `src/components/common/PlatformDarkModalFooter.tsx`;该组件只负责 dark footer 的分隔线、padding 和常见动作区排布,不持有“取消 / 确认”业务语义。`NpcModals.tsx` 的交易 / 赠礼 / 招募 footer、`SelectionCustomizationModals.tsx` 的 `SelectionModal` footer、`RpgAdventurePanelOverlays.tsx` 的 goal panel footer,以及 `InventoryItemViews.tsx` 的详情 footer wrapper 已接入;sticky 工作台 footer、正文内单 CTA 收尾和 runtime HUD 工具条暂不并入这一抽象。
|
||||||
|
- 2026-06-11 追加:桌面首页里的轻量可点击扁平行开始统一收口到 `src/components/common/PlatformNavigableListItem.tsx`;这一轮只覆盖 `RpgEntryHomeView.tsx` 的搜索结果行、桌面“最近作品”和“最近浏览”行。组件只承接 `button + left content + right affordance` 结构、默认 `type="button"` 与 `leading / trailing` 插槽,暂不扩成覆盖趋势卡、教培 promo card、分类卡片或 runtime 列表项的万能 row primitive。
|
||||||
- 2026-06-09 追加:通用输入 Composer 的上传参考图、发送和移除参考图已迁移到 `PlatformIconButton`;图标上传仍使用 `asChild="label"` 保留 label + file input 语义,公共组件会自动写入隐藏文本,确保内嵌 file input 继承可访问名称。
|
- 2026-06-09 追加:通用输入 Composer 的上传参考图、发送和移除参考图已迁移到 `PlatformIconButton`;图标上传仍使用 `asChild="label"` 保留 label + file input 语义,公共组件会自动写入隐藏文本,确保内嵌 file input 继承可访问名称。
|
||||||
- 2026-06-10 追加:creation-agent composer 的上传文档 / 上传参考图入口使用 `PlatformIconButton` 默认 `platformIcon`;工作台只保留动态 label、title、busy 状态和 picker 回调,发送按钮继续保留主题色动作布局。验证命令:`npm run test -- src/components/creation-agent/CreationAgentWorkspace.test.tsx src/components/common/PlatformIconButton.test.tsx`。
|
- 2026-06-10 追加:creation-agent composer 的上传文档 / 上传参考图入口使用 `PlatformIconButton` 默认 `platformIcon`;工作台只保留动态 label、title、busy 状态和 picker 回调,发送按钮继续保留主题色动作布局。验证命令:`npm run test -- src/components/creation-agent/CreationAgentWorkspace.test.tsx src/components/common/PlatformIconButton.test.tsx`。
|
||||||
- 2026-06-10 追加:作品详情顶部返回 / 分享和封面轮播上一张 / 下一张入口使用 `PlatformIconButton variant="platformIcon"`;详情页保留原 `platform-work-detail__*` 局部 class 控制位置和尺寸,点赞、复制三态等专用动作暂不迁移。验证命令:`npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformIconButton.test.tsx`。
|
- 2026-06-10 追加:作品详情顶部返回 / 分享和封面轮播上一张 / 下一张入口使用 `PlatformIconButton variant="platformIcon"`;详情页保留原 `platform-work-detail__*` 局部 class 控制位置和尺寸,点赞、复制三态等专用动作暂不迁移。验证命令:`npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformIconButton.test.tsx`。
|
||||||
|
|||||||
@@ -260,6 +260,7 @@
|
|||||||
19.3.36. `VisualNovelEntityGrid` 的空态也继续收口到 `PlatformEmptyState surface="subpanel" size="inline"`;角色 / 场景 / 剧情阶段共用这一网格组件后,白底实体列表里的“暂无角色 / 暂无场景 / 暂无剧情阶段”等同构空态不再回退成 `PlatformSubpanel`。同时,`RpgCreationRoleAssetStudioModalImpl.tsx` 与 `RpgCreationEntityEditorShared.tsx` 保留局部 `ActionButton` 语义壳,但按钮本体已统一委托给 `PlatformActionButton surface="editorDark"`,只在包装层补最小的 `stopPropagation`、tone 映射和局部 class 适配。后续类似“暗色编辑器局部包装按钮”优先沿用这种薄包装模式,不再直接手写原生 `<button>` 基础 chrome。验证命令:`npm run test -- src/components/visual-novel-result/VisualNovelResultView.test.tsx src/components/common/PlatformEmptyState.test.tsx src/components/rpg-creation-asset-studio/RpgCreationRoleAssetStudioModal.test.tsx src/components/CustomWorldEntityEditorModal.test.tsx src/components/common/PlatformActionButton.test.tsx`、`npm run check:encoding`。
|
19.3.36. `VisualNovelEntityGrid` 的空态也继续收口到 `PlatformEmptyState surface="subpanel" size="inline"`;角色 / 场景 / 剧情阶段共用这一网格组件后,白底实体列表里的“暂无角色 / 暂无场景 / 暂无剧情阶段”等同构空态不再回退成 `PlatformSubpanel`。同时,`RpgCreationRoleAssetStudioModalImpl.tsx` 与 `RpgCreationEntityEditorShared.tsx` 保留局部 `ActionButton` 语义壳,但按钮本体已统一委托给 `PlatformActionButton surface="editorDark"`,只在包装层补最小的 `stopPropagation`、tone 映射和局部 class 适配。后续类似“暗色编辑器局部包装按钮”优先沿用这种薄包装模式,不再直接手写原生 `<button>` 基础 chrome。验证命令:`npm run test -- src/components/visual-novel-result/VisualNovelResultView.test.tsx src/components/common/PlatformEmptyState.test.tsx src/components/rpg-creation-asset-studio/RpgCreationRoleAssetStudioModal.test.tsx src/components/CustomWorldEntityEditorModal.test.tsx src/components/common/PlatformActionButton.test.tsx`、`npm run check:encoding`。
|
||||||
19.3.37. 暗色编辑器里的局部动作按钮继续往共享 `editorDark` button 收口:`CustomWorldNpcVisualEditor.tsx` 的本地 `ActionButton` 和 `SkillEffectPreview.tsx` 的“重新预览”按钮都改为委托 `PlatformActionButton surface="editorDark"`。这类局部包装仍可保留 `stopPropagation`、图标布局、`tone` 映射和少量局部视觉覆写,但按钮本体不再直接使用原生 `<button>` 承接边框 / 底色 / hover / disabled chrome。验证命令:`npm run test -- src/components/common/PlatformActionButton.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
|
19.3.37. 暗色编辑器里的局部动作按钮继续往共享 `editorDark` button 收口:`CustomWorldNpcVisualEditor.tsx` 的本地 `ActionButton` 和 `SkillEffectPreview.tsx` 的“重新预览”按钮都改为委托 `PlatformActionButton surface="editorDark"`。这类局部包装仍可保留 `stopPropagation`、图标布局、`tone` 映射和少量局部视觉覆写,但按钮本体不再直接使用原生 `<button>` 承接边框 / 底色 / hover / disabled chrome。验证命令:`npm run test -- src/components/common/PlatformActionButton.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
|
||||||
19.3.38. `PlatformAsyncStatePanel` 继续从 profile / 作品架扩展到首页公开分区:`RpgEntryHomeView.tsx` 的移动端排行、发现页寓教于乐 / 默认公开 feed、桌面首页“今日游戏 / 推荐”、桌面发现页寓教于乐 / 默认公开 feed,以及“我的创作”分区都统一改成 `loadingState / emptyState / children` 三个 slot 切换。页面继续把 `platformError` 保留在状态壳外层,让错误提示可以和内容并存;`recommend runtime`、分类筛选和其它含二级筛选 / 运行态语义的分支暂不并入这次收口。后续首页、作品架或白底列表若只是纯 `loading / empty / content` 互斥状态,优先直接复用 `PlatformAsyncStatePanel`,不要再把空态与读取态分支手写回业务 JSX。验证命令:`npx vitest run src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
|
19.3.38. `PlatformAsyncStatePanel` 继续从 profile / 作品架扩展到首页公开分区:`RpgEntryHomeView.tsx` 的移动端排行、发现页寓教于乐 / 默认公开 feed、桌面首页“今日游戏 / 推荐”、桌面发现页寓教于乐 / 默认公开 feed,以及“我的创作”分区都统一改成 `loadingState / emptyState / children` 三个 slot 切换。页面继续把 `platformError` 保留在状态壳外层,让错误提示可以和内容并存;`recommend runtime`、分类筛选和其它含二级筛选 / 运行态语义的分支暂不并入这次收口。后续首页、作品架或白底列表若只是纯 `loading / empty / content` 互斥状态,优先直接复用 `PlatformAsyncStatePanel`,不要再把空态与读取态分支手写回业务 JSX。验证命令:`npx vitest run src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
|
||||||
|
19.3.39. 桌面首页里的轻量可点击扁平行统一收口到 `src/components/common/PlatformNavigableListItem.tsx`;该 Module 只承接 `button + 左侧主内容 + 右侧 affordance` 的结构、默认 `type="button"` 和 `leading / trailing` 插槽,不承接卡片封面、复杂摘要或 runtime 专属交互。`RpgEntryHomeView.tsx` 的搜索结果行、桌面“最近作品”和“最近浏览”行已接入。`DesktopTrendingItem`、教培 promo card、分类卡片、世界卡和 runtime 列表项继续保留各自语义,等出现更多同构 desktop flat row 再逐步扩覆盖面。验证命令:`npx vitest run src/components/common/PlatformNavigableListItem.test.tsx src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
|
||||||
19.3. creative-agent 首页的侧边栏菜单、账号入口、开启新对话、我的创作、首页激励 CTA 和 prompt suggestion 按钮迁移到 `PlatformIconButton` / `PlatformActionButton`;首页继续保留 `creative-agent-home__*` 本地 class 承接透明顶栏、抽屉和品牌化胶囊视觉,不把视觉回收和语义收口绑成一次大改。`Beta` 徽标和历史记录纯文本行暂保留本地实现,等出现更多同构轻量列表行后再评估是否抽新的共享 row primitive。
|
19.3. creative-agent 首页的侧边栏菜单、账号入口、开启新对话、我的创作、首页激励 CTA 和 prompt suggestion 按钮迁移到 `PlatformIconButton` / `PlatformActionButton`;首页继续保留 `creative-agent-home__*` 本地 class 承接透明顶栏、抽屉和品牌化胶囊视觉,不把视觉回收和语义收口绑成一次大改。`Beta` 徽标和历史记录纯文本行暂保留本地实现,等出现更多同构轻量列表行后再评估是否抽新的共享 row primitive。
|
||||||
19.4. 大鱼吃小鱼结果页 hero 的返回入口迁移到 `PlatformIconButton variant="darkMini"`,测试 / 发布动作迁移到 `PlatformActionButton surface="editorDark"`;结果页只保留测试运行、发布提交和文案状态语义,不再手写 hero 顶栏按钮壳。
|
19.4. 大鱼吃小鱼结果页 hero 的返回入口迁移到 `PlatformIconButton variant="darkMini"`,测试 / 发布动作迁移到 `PlatformActionButton surface="editorDark"`;结果页只保留测试运行、发布提交和文案状态语义,不再手写 hero 顶栏按钮壳。
|
||||||
19.4.1. 大鱼吃小鱼结果页的发布失败弹层迁移到 `src/components/common/PlatformStatusDialog.tsx`;`PlatformStatusDialog` 补充自定义图标、可访问标签和动作按钮样式透传后,`BigFishResultView` 不再保留 `BigFishResultErrorModal` 内联的 `UnifiedConfirmDialog + PlatformIconBadge` 组合。结果页只保留失败文案和关闭回调,发布失败的状态图标、遮罩、白底面板和“知道了”主动作统一由共享状态弹层承接。验证命令:`npm run test -- src/components/common/PlatformStatusDialog.test.tsx src/components/big-fish-result/BigFishResultView.test.tsx`、`npm run typecheck`。
|
19.4.1. 大鱼吃小鱼结果页的发布失败弹层迁移到 `src/components/common/PlatformStatusDialog.tsx`;`PlatformStatusDialog` 补充自定义图标、可访问标签和动作按钮样式透传后,`BigFishResultView` 不再保留 `BigFishResultErrorModal` 内联的 `UnifiedConfirmDialog + PlatformIconBadge` 组合。结果页只保留失败文案和关闭回调,发布失败的状态图标、遮罩、白底面板和“知道了”主动作统一由共享状态弹层承接。验证命令:`npm run test -- src/components/common/PlatformStatusDialog.test.tsx src/components/big-fish-result/BigFishResultView.test.tsx`、`npm run typecheck`。
|
||||||
|
|||||||
48
src/components/common/PlatformNavigableListItem.test.tsx
Normal file
48
src/components/common/PlatformNavigableListItem.test.tsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/* @vitest-environment jsdom */
|
||||||
|
|
||||||
|
import { fireEvent, render, screen } from '@testing-library/react';
|
||||||
|
import { describe, expect, test, vi } from 'vitest';
|
||||||
|
|
||||||
|
import { PlatformNavigableListItem } from './PlatformNavigableListItem';
|
||||||
|
|
||||||
|
describe('PlatformNavigableListItem', () => {
|
||||||
|
test('renders shared navigable row structure with leading and trailing slots', () => {
|
||||||
|
render(
|
||||||
|
<PlatformNavigableListItem
|
||||||
|
align="start"
|
||||||
|
leading={<span data-testid="leading">L</span>}
|
||||||
|
trailing={<span data-testid="trailing">R</span>}
|
||||||
|
>
|
||||||
|
<span>行内容</span>
|
||||||
|
</PlatformNavigableListItem>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const button = screen.getByRole('button', { name: /L\s+行内容\s+R/u });
|
||||||
|
|
||||||
|
expect(button.className).toContain('platform-navigable-list-item');
|
||||||
|
expect(button.className).toContain('items-start');
|
||||||
|
expect(button.className).toContain('gap-4');
|
||||||
|
expect(screen.getByTestId('leading').parentElement?.className).toContain(
|
||||||
|
'platform-navigable-list-item__leading',
|
||||||
|
);
|
||||||
|
expect(screen.getByTestId('trailing').parentElement?.className).toContain(
|
||||||
|
'platform-navigable-list-item__trailing',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('defaults to type button and forwards click handlers', () => {
|
||||||
|
const onClick = vi.fn();
|
||||||
|
|
||||||
|
render(
|
||||||
|
<PlatformNavigableListItem onClick={onClick}>
|
||||||
|
<span>打开详情</span>
|
||||||
|
</PlatformNavigableListItem>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const button = screen.getByRole('button', { name: '打开详情' });
|
||||||
|
|
||||||
|
expect(button.getAttribute('type')).toBe('button');
|
||||||
|
fireEvent.click(button);
|
||||||
|
expect(onClick).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
88
src/components/common/PlatformNavigableListItem.tsx
Normal file
88
src/components/common/PlatformNavigableListItem.tsx
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import type { ComponentPropsWithoutRef, ReactNode } from 'react';
|
||||||
|
|
||||||
|
type PlatformNavigableListItemAlign = 'center' | 'start';
|
||||||
|
|
||||||
|
type PlatformNavigableListItemProps = Omit<
|
||||||
|
ComponentPropsWithoutRef<'button'>,
|
||||||
|
'children' | 'type'
|
||||||
|
> & {
|
||||||
|
children: ReactNode;
|
||||||
|
leading?: ReactNode;
|
||||||
|
trailing?: ReactNode;
|
||||||
|
align?: PlatformNavigableListItemAlign;
|
||||||
|
leadingClassName?: string;
|
||||||
|
trailingClassName?: string;
|
||||||
|
bodyClassName?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ALIGN_CLASS: Record<PlatformNavigableListItemAlign, string> = {
|
||||||
|
center: 'items-center gap-3',
|
||||||
|
start: 'items-start gap-4',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 轻量可导航列表行骨架。
|
||||||
|
* 只统一 button 语义与 left-content/right-affordance 结构,不持有业务文案、badge 或封面规则。
|
||||||
|
*/
|
||||||
|
export function PlatformNavigableListItem({
|
||||||
|
children,
|
||||||
|
leading = null,
|
||||||
|
trailing = null,
|
||||||
|
align = 'center',
|
||||||
|
className,
|
||||||
|
leadingClassName,
|
||||||
|
trailingClassName,
|
||||||
|
bodyClassName,
|
||||||
|
...props
|
||||||
|
}: PlatformNavigableListItemProps) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={[
|
||||||
|
'platform-navigable-list-item flex w-full min-w-0 text-left',
|
||||||
|
ALIGN_CLASS[align],
|
||||||
|
className ?? null,
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ')}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{leading ? (
|
||||||
|
<div
|
||||||
|
className={[
|
||||||
|
'platform-navigable-list-item__leading shrink-0',
|
||||||
|
leadingClassName ?? null,
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ')}
|
||||||
|
>
|
||||||
|
{leading}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={[
|
||||||
|
'platform-navigable-list-item__body min-w-0 flex-1',
|
||||||
|
bodyClassName ?? null,
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ')}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{trailing ? (
|
||||||
|
<div
|
||||||
|
className={[
|
||||||
|
'platform-navigable-list-item__trailing shrink-0',
|
||||||
|
trailingClassName ?? null,
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ')}
|
||||||
|
>
|
||||||
|
{trailing}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ import type {
|
|||||||
import type {
|
import type {
|
||||||
ConfirmWechatProfileRechargeOrderResponse,
|
ConfirmWechatProfileRechargeOrderResponse,
|
||||||
CreateProfileRechargeOrderResponse,
|
CreateProfileRechargeOrderResponse,
|
||||||
|
PlatformBrowseHistoryEntry,
|
||||||
ProfilePlayStatsResponse,
|
ProfilePlayStatsResponse,
|
||||||
ProfileReferralInviteCenterResponse,
|
ProfileReferralInviteCenterResponse,
|
||||||
ProfileTaskCenterResponse,
|
ProfileTaskCenterResponse,
|
||||||
@@ -902,6 +903,7 @@ function renderLoggedOutHomeView(
|
|||||||
| 'onOpenRecommendGalleryDetail'
|
| 'onOpenRecommendGalleryDetail'
|
||||||
| 'onOpenChildMotionDemo'
|
| 'onOpenChildMotionDemo'
|
||||||
| 'onSearchPublicCode'
|
| 'onSearchPublicCode'
|
||||||
|
| 'historyEntries'
|
||||||
| 'recommendRuntimeContent'
|
| 'recommendRuntimeContent'
|
||||||
| 'activeRecommendEntryKey'
|
| 'activeRecommendEntryKey'
|
||||||
| 'isStartingRecommendEntry'
|
| 'isStartingRecommendEntry'
|
||||||
@@ -945,7 +947,7 @@ function renderLoggedOutHomeView(
|
|||||||
featuredEntries={overrides.featuredEntries ?? []}
|
featuredEntries={overrides.featuredEntries ?? []}
|
||||||
latestEntries={overrides.latestEntries ?? []}
|
latestEntries={overrides.latestEntries ?? []}
|
||||||
myEntries={[]}
|
myEntries={[]}
|
||||||
historyEntries={[]}
|
historyEntries={overrides.historyEntries ?? []}
|
||||||
profileDashboard={null}
|
profileDashboard={null}
|
||||||
isLoadingPlatform={false}
|
isLoadingPlatform={false}
|
||||||
isLoadingDashboard={false}
|
isLoadingDashboard={false}
|
||||||
@@ -3746,7 +3748,14 @@ test('discover search fuzzy matches public work id, name, author and description
|
|||||||
|
|
||||||
await user.clear(searchInput);
|
await user.clear(searchInput);
|
||||||
await user.type(searchInput, '火桥{enter}');
|
await user.type(searchInput, '火桥{enter}');
|
||||||
expect(await within(discoverPanel).findByText('火桥谜图')).toBeTruthy();
|
const lavaBridgeResultButton = await within(discoverPanel).findByRole(
|
||||||
|
'button',
|
||||||
|
{ name: /火桥谜图/u },
|
||||||
|
);
|
||||||
|
expect(lavaBridgeResultButton.className).toContain(
|
||||||
|
'platform-navigable-list-item',
|
||||||
|
);
|
||||||
|
expect(within(discoverPanel).getByText('火桥谜图')).toBeTruthy();
|
||||||
expect(within(discoverPanel).queryByText('月井机关')).toBeNull();
|
expect(within(discoverPanel).queryByText('月井机关')).toBeNull();
|
||||||
|
|
||||||
await user.clear(searchInput);
|
await user.clear(searchInput);
|
||||||
@@ -5336,6 +5345,41 @@ test('desktop logged in home syncs mobile home modules without square or latest
|
|||||||
expect(screen.queryByText('1777110165.990127Z')).toBeNull();
|
expect(screen.queryByText('1777110165.990127Z')).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('desktop home quick access rows reuse shared navigable list item', async () => {
|
||||||
|
mockDesktopLayout();
|
||||||
|
const user = userEvent.setup();
|
||||||
|
const onOpenRecommendGalleryDetail = vi.fn();
|
||||||
|
const historyEntry = {
|
||||||
|
ownerUserId: 'history-user-1',
|
||||||
|
profileId: 'history-profile-1',
|
||||||
|
worldName: '桌面最近浏览作品',
|
||||||
|
subtitle: '历史副标题',
|
||||||
|
summaryText: '这是最近浏览入口。',
|
||||||
|
coverImageSrc: null,
|
||||||
|
themeMode: 'mythic',
|
||||||
|
authorDisplayName: '历史作者',
|
||||||
|
visitedAt: new Date().toISOString(),
|
||||||
|
} satisfies PlatformBrowseHistoryEntry;
|
||||||
|
|
||||||
|
renderLoggedOutHomeView(
|
||||||
|
vi.fn(),
|
||||||
|
{
|
||||||
|
historyEntries: [historyEntry],
|
||||||
|
onOpenRecommendGalleryDetail,
|
||||||
|
},
|
||||||
|
'home',
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
const historyButton = screen.getByRole('button', {
|
||||||
|
name: /桌面最近浏览作品/u,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(historyButton.className).toContain('platform-navigable-list-item');
|
||||||
|
await user.click(historyButton);
|
||||||
|
expect(onOpenRecommendGalleryDetail).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
test('mobile home moves category shelf into game category channel', async () => {
|
test('mobile home moves category shelf into game category channel', async () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
const { container } = renderStatefulLoggedOutHomeView({
|
const { container } = renderStatefulLoggedOutHomeView({
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ import { PlatformFieldLabel } from '../common/PlatformFieldLabel';
|
|||||||
import { PlatformIconBadge } from '../common/PlatformIconBadge';
|
import { PlatformIconBadge } from '../common/PlatformIconBadge';
|
||||||
import { PlatformIconButton } from '../common/PlatformIconButton';
|
import { PlatformIconButton } from '../common/PlatformIconButton';
|
||||||
import { PlatformModalCloseButton } from '../common/PlatformModalCloseButton';
|
import { PlatformModalCloseButton } from '../common/PlatformModalCloseButton';
|
||||||
|
import { PlatformNavigableListItem } from '../common/PlatformNavigableListItem';
|
||||||
import { PlatformPillBadge } from '../common/PlatformPillBadge';
|
import { PlatformPillBadge } from '../common/PlatformPillBadge';
|
||||||
import { PlatformSegmentedTabs } from '../common/PlatformSegmentedTabs';
|
import { PlatformSegmentedTabs } from '../common/PlatformSegmentedTabs';
|
||||||
import { PlatformStatusDialog } from '../common/PlatformStatusDialog';
|
import { PlatformStatusDialog } from '../common/PlatformStatusDialog';
|
||||||
@@ -1918,7 +1919,7 @@ function DesktopTrendingItem({
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className="platform-desktop-trending-item flex items-center gap-4 px-4 py-4 text-left"
|
className="platform-desktop-trending-item flex items-start gap-4 px-4 py-4 text-left"
|
||||||
>
|
>
|
||||||
<div className="relative h-[5.5rem] w-[4.3rem] shrink-0 overflow-hidden rounded-[1rem] border border-[var(--platform-subpanel-border)] bg-[rgba(255,255,255,0.66)]">
|
<div className="relative h-[5.5rem] w-[4.3rem] shrink-0 overflow-hidden rounded-[1rem] border border-[var(--platform-subpanel-border)] bg-[rgba(255,255,255,0.66)]">
|
||||||
{coverImage ? (
|
{coverImage ? (
|
||||||
@@ -2282,11 +2283,19 @@ function PlatformWorkSearchResults({
|
|||||||
entry.summaryText || entry.subtitle || '等待补充世界摘要。';
|
entry.summaryText || entry.subtitle || '等待补充世界摘要。';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<PlatformNavigableListItem
|
||||||
key={`${buildPublicGalleryCardKey(entry)}:search-result`}
|
key={`${buildPublicGalleryCardKey(entry)}:search-result`}
|
||||||
type="button"
|
|
||||||
onClick={() => onOpen(entry)}
|
onClick={() => onOpen(entry)}
|
||||||
className="platform-desktop-trending-item flex w-full items-center justify-between gap-3 px-4 py-3 text-left"
|
className="platform-desktop-trending-item px-4 py-3"
|
||||||
|
trailing={
|
||||||
|
<PlatformPillBadge
|
||||||
|
tone="neutral"
|
||||||
|
size="xxs"
|
||||||
|
className="shrink-0 px-3 tracking-[0.18em]"
|
||||||
|
>
|
||||||
|
{describePlatformPublicWorkKind(entry)}
|
||||||
|
</PlatformPillBadge>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<div className="line-clamp-1 text-sm font-semibold text-[var(--platform-text-strong)]">
|
<div className="line-clamp-1 text-sm font-semibold text-[var(--platform-text-strong)]">
|
||||||
@@ -2300,14 +2309,7 @@ function PlatformWorkSearchResults({
|
|||||||
{summaryText}
|
{summaryText}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<PlatformPillBadge
|
</PlatformNavigableListItem>
|
||||||
tone="neutral"
|
|
||||||
size="xxs"
|
|
||||||
className="shrink-0 px-3 tracking-[0.18em]"
|
|
||||||
>
|
|
||||||
{describePlatformPublicWorkKind(entry)}
|
|
||||||
</PlatformPillBadge>
|
|
||||||
</button>
|
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
@@ -4723,11 +4725,21 @@ export function RpgEntryHomeView({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<PlatformNavigableListItem
|
||||||
key={`${entry.ownerUserId}:${entry.profileId}:desktop-mine`}
|
key={`${entry.ownerUserId}:${entry.profileId}:desktop-mine`}
|
||||||
type="button"
|
|
||||||
onClick={() => onOpenLibraryDetail(entry)}
|
onClick={() => onOpenLibraryDetail(entry)}
|
||||||
className="platform-desktop-trending-item flex w-full items-center justify-between gap-3 px-4 py-4 text-left"
|
className="platform-desktop-trending-item px-4 py-4"
|
||||||
|
trailing={
|
||||||
|
<PlatformPillBadge
|
||||||
|
tone="neutral"
|
||||||
|
size="xxs"
|
||||||
|
className="px-3 tracking-[0.18em]"
|
||||||
|
>
|
||||||
|
{entry.visibility === 'published'
|
||||||
|
? '已发布'
|
||||||
|
: '草稿'}
|
||||||
|
</PlatformPillBadge>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<div className="line-clamp-1 text-base font-semibold text-[var(--platform-text-strong)]">
|
<div className="line-clamp-1 text-base font-semibold text-[var(--platform-text-strong)]">
|
||||||
@@ -4739,16 +4751,7 @@ export function RpgEntryHomeView({
|
|||||||
: '草稿待完善'}
|
: '草稿待完善'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<PlatformPillBadge
|
</PlatformNavigableListItem>
|
||||||
tone="neutral"
|
|
||||||
size="xxs"
|
|
||||||
className="px-3 tracking-[0.18em]"
|
|
||||||
>
|
|
||||||
{entry.visibility === 'published'
|
|
||||||
? '已发布'
|
|
||||||
: '草稿'}
|
|
||||||
</PlatformPillBadge>
|
|
||||||
</button>
|
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
@@ -4760,9 +4763,8 @@ export function RpgEntryHomeView({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<PlatformNavigableListItem
|
||||||
key={`${entry.ownerUserId}:${entry.profileId}:desktop-history`}
|
key={`${entry.ownerUserId}:${entry.profileId}:desktop-history`}
|
||||||
type="button"
|
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
openRecommendGalleryDetail({
|
openRecommendGalleryDetail({
|
||||||
ownerUserId: entry.ownerUserId,
|
ownerUserId: entry.ownerUserId,
|
||||||
@@ -4783,7 +4785,16 @@ export function RpgEntryHomeView({
|
|||||||
likeCount: 0,
|
likeCount: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
className="platform-desktop-trending-item flex w-full items-center justify-between gap-3 px-4 py-4 text-left"
|
className="platform-desktop-trending-item px-4 py-4"
|
||||||
|
trailing={
|
||||||
|
<PlatformPillBadge
|
||||||
|
tone="neutral"
|
||||||
|
size="xxs"
|
||||||
|
className="px-3 tracking-[0.18em]"
|
||||||
|
>
|
||||||
|
浏览
|
||||||
|
</PlatformPillBadge>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<div className="line-clamp-1 text-base font-semibold text-[var(--platform-text-strong)]">
|
<div className="line-clamp-1 text-base font-semibold text-[var(--platform-text-strong)]">
|
||||||
@@ -4793,14 +4804,7 @@ export function RpgEntryHomeView({
|
|||||||
作者:{entry.authorDisplayName}
|
作者:{entry.authorDisplayName}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<PlatformPillBadge
|
</PlatformNavigableListItem>
|
||||||
tone="neutral"
|
|
||||||
size="xxs"
|
|
||||||
className="px-3 tracking-[0.18em]"
|
|
||||||
>
|
|
||||||
浏览
|
|
||||||
</PlatformPillBadge>
|
|
||||||
</button>
|
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user