fix(ui): align desktop update tag styling
This commit is contained in:
1 parent
6446944fa6
commit
422ded6f7a
7 files changed
+34
-31
No files matched your search
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/client/ui-settings-general/README.md
|
||||
README.md: e06ecf5f36234f634cbff708f592fbe83c9f175e
|
||||
README.zh.md: 6eb54b49a7a7f1a55a6f9389c57f736533715185
|
||||
README.md: a500722e765e66e55efa83c9fc12c50756ef37d9
|
||||
README.zh.md: 28f3a39260c4ccaaf844d0d457fc85e39214bb7c
|
||||
@@ -29,7 +29,7 @@ Users reach the shell through the sidebar's bottom Settings control; feature plu
|
||||
|
||||
When the section navigation exceeds the panel's available height, the list scrolls independently of the settings content and keeps the Settings title fixed.
|
||||
|
||||
In Desktop, the account-row update control shows availability, progress, verification, readiness, and persistent retry feedback. The preload carries semantic phase, version, progress, and classified failures; the component resolves every visible and accessible string from the active `settings` locale, including after an in-application language change. Selecting an available update starts downloading; installation requires a separate shell-owned confirmation. A collapsed sidebar shows the same status as a dot on its top expand button. Connection feedback takes priority except during shell-reported installation, when the expected backend disconnect must not hide update status. Failure restores connection feedback. Both controls share one carrier subscription; browser code cannot choose packages or authorize installation. [Desktop updates](../../../apps/desktop/README.md) owns the release workflow.
|
||||
In Desktop, the account-row update control shows availability, progress, verification, readiness, and persistent retry feedback. It shares the connection indicator’s 28px height, 13px corners, 14px icon slot, 4px icon gap, and 12px medium text with an 18px line height; update-state border, fill, and text colors remain independently defined. Retry text and its dot use the same brand blue as other update labels; numeric download progress has no ellipsis. The preload carries semantic phase, version, progress, and classified failures; the component resolves every visible and accessible string from the active `settings` locale, including after an in-application language change. Selecting an available update starts downloading; installation requires a separate shell-owned confirmation. A collapsed sidebar shows the same status as a dot on its top expand button. Connection feedback takes priority except during shell-reported installation, when the expected backend disconnect must not hide update status. Failure restores connection feedback. Both controls share one carrier subscription; browser code cannot choose packages or authorize installation. [Desktop updates](../../../apps/desktop/README.md) owns the release workflow.
|
||||
|
||||
### The General section
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ kind: "package-reference"
|
||||
|
||||
当分区导航超出面板可用高度时,列表独立于设置内容滚动,并保持「设置」标题固定。
|
||||
|
||||
在 Desktop 中,账户行更新控件显示可用版本、进度、验证、就绪和持久重试反馈。预加载仅传递语义化阶段、版本、进度和失败类别;组件从当前 `settings` locale 解析全部可见与无障碍文案,并在应用内语言变化后同步更新。选择可用更新即开始下载;安装需要独立的壳拥有的确认。侧栏收起时,其顶部展开按钮以圆点显示相同状态。连接反馈通常优先展示,但壳报告正在安装时,预期的后端断开不能遮盖更新状态;失败后恢复连接反馈。两个控件共用一次载体订阅;浏览器代码不能选择安装包或授权安装。[Desktop 更新](../../../apps/desktop/README.zh.md)负责发布流程。
|
||||
在 Desktop 中,账户行更新控件显示可用版本、进度、验证、就绪和持久重试反馈。它与连接指示器共用 28px 高度、13px 圆角、14px 图标占位、4px 图文间距,以及 12px 中等字重文字和 18px 行高;更新状态的边框、填充和文字颜色独立定义。重试文字及其圆点与其他更新文案使用相同的品牌蓝色;数字下载进度不带省略号。预加载仅传递语义化阶段、版本、进度和失败类别;组件从当前 `settings` locale 解析全部可见与无障碍文案,并在应用内语言变化后同步更新。选择可用更新即开始下载;安装需要独立的壳拥有的确认。侧栏收起时,其顶部展开按钮以圆点显示相同状态。连接反馈通常优先展示,但壳报告正在安装时,预期的后端断开不能遮盖更新状态;失败后恢复连接反馈。两个控件共用一次载体订阅;浏览器代码不能选择安装包或授权安装。[Desktop 更新](../../../apps/desktop/README.zh.md)负责发布流程。
|
||||
|
||||
### 「通用」分区
|
||||
|
||||
|
||||
@@ -1,28 +1,30 @@
|
||||
/* The entry's seat is the Settings foot row, which bleeds 2px past the sidebar
|
||||
content box for the Settings hover surface. The New Session bar and panel
|
||||
rows instead keep a 2px inset inside that box, so this entry gives back both
|
||||
2px offsets and ends on their trailing edge rather than flush against the
|
||||
sidebar edge. */
|
||||
.indicator {
|
||||
display: inline-flex;
|
||||
display: inline-grid;
|
||||
grid-template-columns: 14px max-content;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
column-gap: 4px;
|
||||
flex: none;
|
||||
margin-inline-end: 4px;
|
||||
padding: 1px 8px;
|
||||
height: 28px;
|
||||
padding: 0 8px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 20%, transparent);
|
||||
border-radius: 6px;
|
||||
border-radius: 13px;
|
||||
color: var(--dsw-alias-brand-primary-new-colorprimary-new-color);
|
||||
background: color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 8%, transparent);
|
||||
font: inherit;
|
||||
font-family: inherit;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
font-weight: 500;
|
||||
line-height: 18px;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.indicator[data-error] {
|
||||
color: var(--dsw-alias-state-error-primary);
|
||||
.icon {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
@@ -36,8 +38,8 @@
|
||||
background: var(--dsw-alias-brand-primary-new-colorprimary-new-color);
|
||||
}
|
||||
|
||||
.badge[data-error], .errorDot { background: var(--dsw-alias-state-error-primary); }
|
||||
.errorDot { width: 6px; height: 6px; border-radius: 50%; corner-shape: round; }
|
||||
.badge[data-error] { background: var(--dsw-alias-state-error-primary); }
|
||||
.errorDot { background: currentColor; width: 6px; height: 6px; border-radius: 50%; corner-shape: round; }
|
||||
.spinner { animation: spin 1s linear infinite; }
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
@media (prefers-reduced-motion: reduce) { .spinner { animation: none; } }
|
||||
|
||||
@@ -46,6 +46,7 @@ function updateCopy(state: DesktopUpdatePresentation, t: SettingsTranslate): { l
|
||||
}
|
||||
|
||||
/**
|
||||
* Render update status with connection-indicator geometry and brand-blue labels, including retries.
|
||||
* @param props - Connection priority, sidebar width, and localized bridge-failure copy.
|
||||
* @returns Desktop-only status beside the account button, or nothing in browsers.
|
||||
*/
|
||||
@@ -64,10 +65,12 @@ export function DesktopUpdateIndicator({ wide, hidden, t, view, onOpen }: {
|
||||
const error = failed || state?.phase === 'error'
|
||||
const busy = opening || (state !== undefined && BUSY_PHASES.has(state.phase))
|
||||
return <Tooltip label={failed ? retryLabel : copy.detail} side="top">
|
||||
<button type="button" className={css.indicator} data-error={error || undefined}
|
||||
<button type="button" className={css.indicator}
|
||||
aria-label={label} aria-disabled={busy} onClick={() => { if (!busy) onOpen() }}>
|
||||
{error ? <span className={css.errorDot} aria-hidden="true" />
|
||||
: busy ? <IconLoadingOutlineRegular className={css.spinner} size={16} /> : <IconDownloadOutlineRegular size={14} />}
|
||||
<span className={css.icon} aria-hidden="true">
|
||||
{error ? <span className={css.errorDot} />
|
||||
: busy ? <IconLoadingOutlineRegular className={css.spinner} size={14} /> : <IconDownloadOutlineRegular size={14} />}
|
||||
</span>
|
||||
<span>{label}</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
@@ -5,7 +5,7 @@ export const zh = {
|
||||
'trigger': '设置',
|
||||
'desktop.update.available': '新版本',
|
||||
'desktop.update.checking': '正在检查更新…',
|
||||
'desktop.update.progress': '{percent}%…',
|
||||
'desktop.update.progress': '{percent}%',
|
||||
'desktop.update.verifying': '正在校验更新文件…',
|
||||
'desktop.update.installing': '正在准备重启…',
|
||||
'desktop.update.ready': '安装并重启',
|
||||
@@ -45,7 +45,7 @@ export const en = {
|
||||
'trigger': 'Settings',
|
||||
'desktop.update.available': 'Update',
|
||||
'desktop.update.checking': 'Checking for updates…',
|
||||
'desktop.update.progress': '{percent}%…',
|
||||
'desktop.update.progress': '{percent}%',
|
||||
'desktop.update.verifying': 'Verifying update files…',
|
||||
'desktop.update.installing': 'Preparing to restart…',
|
||||
'desktop.update.ready': 'Install and Restart',
|
||||
|
||||
@@ -71,11 +71,10 @@ it('keeps the newest event, hides for connection priority, and invokes only the
|
||||
expect(screen.queryByRole('button')).toBeNull()
|
||||
await f.emit({ phase: 'downloading', version: available.version, percent: 58 })
|
||||
f.view.rerender(<f.Indicator />)
|
||||
fireEvent.click(screen.getByRole('button', { name: '58%…' }))
|
||||
fireEvent.click(screen.getByRole('button', { name: '58%' }))
|
||||
expect(f.open).toHaveBeenCalledOnce()
|
||||
await f.emit({ phase: 'error', version: available.version, failure: 'download' })
|
||||
const retry = screen.getByRole('button', { name: '重试更新' })
|
||||
expect(retry.getAttribute('data-error')).toBe('true')
|
||||
fireEvent.focus(retry)
|
||||
expect((await screen.findByRole('tooltip')).textContent).toBe('下载更新失败,请重试。')
|
||||
f.view.rerender(<f.Indicator wide={false} />)
|
||||
@@ -90,7 +89,6 @@ it('keeps bridge failures actionable and ignores status completion after unmount
|
||||
f.open.mockRejectedValueOnce(new Error('IPC unavailable'))
|
||||
fireEvent.click(screen.getByRole('button', { name: '重试更新' }))
|
||||
await act(async () => {})
|
||||
expect(screen.getByRole('button').getAttribute('data-error')).toBe('true')
|
||||
f.view.unmount()
|
||||
const late = fixture()
|
||||
late.view.unmount()
|
||||
@@ -143,11 +141,11 @@ it('shows fallback progress and error details when the shell omits optional fiel
|
||||
const f = fixture()
|
||||
try {
|
||||
await f.emit({ phase: 'downloading' })
|
||||
const progress = screen.getByRole('button', { name: '0%…' })
|
||||
const progress = screen.getByRole('button', { name: '0%' })
|
||||
fireEvent.focus(progress)
|
||||
expect((await screen.findByRole('tooltip')).textContent).toBe('0%…')
|
||||
expect((await screen.findByRole('tooltip')).textContent).toBe('0%')
|
||||
await f.emit({ phase: 'downloading', version: '1.0.1' })
|
||||
fireEvent.focus(screen.getByRole('button', { name: '0%…' }))
|
||||
fireEvent.focus(screen.getByRole('button', { name: '0%' }))
|
||||
expect((await screen.findByRole('tooltip')).textContent).toContain('1.0.1')
|
||||
await f.emit({ phase: 'error' })
|
||||
fireEvent.focus(screen.getByRole('button', { name: '重试更新' }))
|
||||
|
||||
Reference in new issue
Block a user