style(ui): unify light mode with pure white background across app shell and usage views
- Set default light background color to pure white across html, body, and app-shell - Replace grayish background, diffuse card shadows, and translucent pills in usage records with pure white surfaces and clean borders - Ensure usage tables and subpage panels render consistently as pure white documents - Update UI accessibility regression tests to assert pure white surfaces
This commit is contained in:
1 parent
f4bba22817
commit
b1fe595f0b
5 files changed
+212
-257
No files matched your search
+1
-1
@@ -15,7 +15,7 @@
|
||||
: window.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
document.documentElement.dataset.theme = theme;
|
||||
document.documentElement.style.colorScheme = theme;
|
||||
document.documentElement.style.backgroundColor = theme === 'dark' ? '#0b0d11' : '#f6f7f5';
|
||||
document.documentElement.style.backgroundColor = theme === 'dark' ? '#0b0d11' : '#ffffff';
|
||||
})();
|
||||
</script>
|
||||
<title>EasyCLIProxyAPI</title>
|
||||
|
||||
+106
-126
@@ -6,7 +6,6 @@ import {
|
||||
Activity,
|
||||
Database,
|
||||
Gauge,
|
||||
ListFilter,
|
||||
Pencil,
|
||||
RefreshCw,
|
||||
Trash2,
|
||||
@@ -248,7 +247,6 @@ export function UsageRecordsPage() {
|
||||
const [source, setSource] = useState('');
|
||||
const [apiKeyHash, setApiKeyHash] = useState('');
|
||||
const [result, setResult] = useState('all');
|
||||
const [advancedFiltersOpen, setAdvancedFiltersOpen] = useState(false);
|
||||
const [page, setPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(50);
|
||||
const [status, setStatus] = useState<CollectorStatus | null>(null);
|
||||
@@ -447,7 +445,6 @@ export function UsageRecordsPage() {
|
||||
};
|
||||
|
||||
const collectorTone = status?.state === 'error' ? 'error' : status?.state === 'collecting' ? 'success' : '';
|
||||
const activeAdvancedFilterCount = [model, provider, source, apiKeyHash].filter(Boolean).length;
|
||||
const hasCurrentSnapshot = loadedScopeKey === scopeKey;
|
||||
const showInitialLoading =
|
||||
activeTab !== 'data-management' &&
|
||||
@@ -471,135 +468,118 @@ export function UsageRecordsPage() {
|
||||
|
||||
const filterPanel = (
|
||||
<section className="panel usage-filter-panel">
|
||||
<div className="usage-filter-header">
|
||||
<span className="usage-filter-title">{t('usage.filter.title')}</span>
|
||||
{activeAdvancedFilterCount > 0 ? (
|
||||
<span className="usage-filter-active">{t('usage.filter.active', { count: activeAdvancedFilterCount })}</span>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="usage-filter-row">
|
||||
<div className="usage-filter-group usage-filter-primary">
|
||||
<div className="usage-filter-group">
|
||||
<label className="usage-filter-item">
|
||||
<span className="usage-filter-label">{t('usage.filter.timeRange')}</span>
|
||||
<span className="sr-only">{t('usage.filter.timeRange')}</span>
|
||||
<select
|
||||
value={range}
|
||||
onChange={(event) => {
|
||||
setRange(event.currentTarget.value as UsageRange);
|
||||
setPage(1);
|
||||
}}
|
||||
aria-label={t('usage.filter.timeRange')}
|
||||
>
|
||||
<option value="4h">{t('usage.range.4h')}</option>
|
||||
<option value="24h">{t('usage.range.24h')}</option>
|
||||
<option value="today">{t('usage.range.today')}</option>
|
||||
<option value="7d">{t('usage.range.7d')}</option>
|
||||
<option value="30d">{t('usage.range.30d')}</option>
|
||||
<option value="all">{t('usage.range.all')}</option>
|
||||
<option value="custom">{t('usage.range.custom')}</option>
|
||||
</select>
|
||||
</label>
|
||||
value={range}
|
||||
onChange={(event) => {
|
||||
setRange(event.currentTarget.value as UsageRange);
|
||||
setPage(1);
|
||||
}}
|
||||
aria-label={t('usage.filter.timeRange')}
|
||||
>
|
||||
<option value="4h">{t('usage.range.4h')}</option>
|
||||
<option value="24h">{t('usage.range.24h')}</option>
|
||||
<option value="today">{t('usage.range.today')}</option>
|
||||
<option value="7d">{t('usage.range.7d')}</option>
|
||||
<option value="30d">{t('usage.range.30d')}</option>
|
||||
<option value="all">{t('usage.range.all')}</option>
|
||||
<option value="custom">{t('usage.range.custom')}</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label className="usage-filter-item">
|
||||
<span className="usage-filter-label">{t('usage.filter.result')}</span>
|
||||
<select
|
||||
value={result}
|
||||
onChange={(event) => changeFilter(setResult, event.currentTarget.value)}
|
||||
aria-label={t('usage.filter.result')}
|
||||
>
|
||||
<option value="all">{t('usage.filter.allResults')}</option>
|
||||
<option value="success">{t('usage.result.success')}</option>
|
||||
<option value="failed">{t('usage.result.failed')}</option>
|
||||
<option value="canceled">{t('usage.result.canceled')}</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className={`usage-filter-toggle${advancedFiltersOpen ? ' is-open' : ''}`}
|
||||
aria-expanded={advancedFiltersOpen}
|
||||
onClick={() => setAdvancedFiltersOpen((open) => !open)}
|
||||
>
|
||||
<ListFilter size={15} aria-hidden="true" />
|
||||
<span>{advancedFiltersOpen ? t('usage.filter.hideMore') : t('usage.filter.more')}</span>
|
||||
{activeAdvancedFilterCount > 0 ? <b>{activeAdvancedFilterCount}</b> : null}
|
||||
</button>
|
||||
<label className="usage-filter-item">
|
||||
<span className="sr-only">{t('usage.filter.model')}</span>
|
||||
<select
|
||||
value={model}
|
||||
onChange={(event) => changeFilter(setModel, event.currentTarget.value)}
|
||||
aria-label={t('usage.filter.model')}
|
||||
>
|
||||
<option value="">{t('usage.filter.allModels')}</option>
|
||||
{filterOptions(optionsAnalysis.models).map((item) => (
|
||||
<option value={item.key} key={item.key}>{item.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label className="usage-filter-item">
|
||||
<span className="sr-only">{t('usage.column.provider')}</span>
|
||||
<select
|
||||
value={provider}
|
||||
onChange={(event) => changeFilter(setProvider, event.currentTarget.value)}
|
||||
aria-label={t('usage.column.provider')}
|
||||
>
|
||||
<option value="">{t('usage.filter.allProviders')}</option>
|
||||
{filterOptions(optionsAnalysis.providers).map((item) => (
|
||||
<option value={item.key} key={item.key}>{item.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label className="usage-filter-item">
|
||||
<span className="sr-only">{t('usage.filter.source')}</span>
|
||||
<select
|
||||
value={source}
|
||||
onChange={(event) => changeFilter(setSource, event.currentTarget.value)}
|
||||
aria-label={t('usage.filter.source')}
|
||||
>
|
||||
<option value="">{t('usage.filter.allSources')}</option>
|
||||
{filterOptions(optionsAnalysis.sources).map((item) => (
|
||||
<option value={item.key} key={item.key}>{item.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label className="usage-filter-item">
|
||||
<span className="sr-only">{t('apiAccess.field.key')}</span>
|
||||
<select
|
||||
value={apiKeyHash}
|
||||
onChange={(event) => changeFilter(setApiKeyHash, event.currentTarget.value)}
|
||||
aria-label={t('apiAccess.field.key')}
|
||||
>
|
||||
<option value="">{t('usage.filter.allKeys')}</option>
|
||||
{filterOptions(optionsAnalysis.apiKeys).map((item) => (
|
||||
<option value={item.key} key={item.key}>{item.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label className="usage-filter-item">
|
||||
<span className="sr-only">{t('usage.filter.result')}</span>
|
||||
<select
|
||||
value={result}
|
||||
onChange={(event) => changeFilter(setResult, event.currentTarget.value)}
|
||||
aria-label={t('usage.filter.result')}
|
||||
>
|
||||
<option value="all">{t('usage.filter.allResults')}</option>
|
||||
<option value="success">{t('usage.result.success')}</option>
|
||||
<option value="failed">{t('usage.result.failed')}</option>
|
||||
<option value="canceled">{t('usage.result.canceled')}</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{advancedFiltersOpen ? (
|
||||
<div className="usage-filter-advanced">
|
||||
<label className="usage-filter-item">
|
||||
<span className="usage-filter-label">{t('usage.filter.model')}</span>
|
||||
<select
|
||||
value={model}
|
||||
onChange={(event) => changeFilter(setModel, event.currentTarget.value)}
|
||||
aria-label={t('usage.filter.model')}
|
||||
>
|
||||
<option value="">{t('usage.filter.allModels')}</option>
|
||||
{filterOptions(optionsAnalysis.models).map((item) => (
|
||||
<option value={item.key} key={item.key}>{item.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label className="usage-filter-item">
|
||||
<span className="usage-filter-label">{t('usage.column.provider')}</span>
|
||||
<select
|
||||
value={provider}
|
||||
onChange={(event) => changeFilter(setProvider, event.currentTarget.value)}
|
||||
aria-label={t('usage.column.provider')}
|
||||
>
|
||||
<option value="">{t('usage.filter.allProviders')}</option>
|
||||
{filterOptions(optionsAnalysis.providers).map((item) => (
|
||||
<option value={item.key} key={item.key}>{item.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label className="usage-filter-item">
|
||||
<span className="usage-filter-label">{t('usage.filter.source')}</span>
|
||||
<select
|
||||
value={source}
|
||||
onChange={(event) => changeFilter(setSource, event.currentTarget.value)}
|
||||
aria-label={t('usage.filter.source')}
|
||||
>
|
||||
<option value="">{t('usage.filter.allSources')}</option>
|
||||
{filterOptions(optionsAnalysis.sources).map((item) => (
|
||||
<option value={item.key} key={item.key}>{item.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label className="usage-filter-item">
|
||||
<span className="usage-filter-label">{t('apiAccess.field.key')}</span>
|
||||
<select
|
||||
value={apiKeyHash}
|
||||
onChange={(event) => changeFilter(setApiKeyHash, event.currentTarget.value)}
|
||||
aria-label={t('apiAccess.field.key')}
|
||||
>
|
||||
<option value="">{t('usage.filter.allKeys')}</option>
|
||||
{filterOptions(optionsAnalysis.apiKeys).map((item) => (
|
||||
<option value={item.key} key={item.key}>{item.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{range === 'custom' ? (
|
||||
<div className="usage-custom-range">
|
||||
<input
|
||||
type="datetime-local"
|
||||
value={customStart}
|
||||
onChange={(event) => setCustomStart(event.currentTarget.value)}
|
||||
aria-label={t('usage.filter.startTime')}
|
||||
/>
|
||||
<span>{t('usage.filter.to')}</span>
|
||||
<input
|
||||
type="datetime-local"
|
||||
value={customEnd}
|
||||
onChange={(event) => setCustomEnd(event.currentTarget.value)}
|
||||
aria-label={t('usage.filter.endTime')}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</section>
|
||||
{range === 'custom' ? (
|
||||
<div className="usage-custom-range">
|
||||
<input
|
||||
type="datetime-local"
|
||||
value={customStart}
|
||||
onChange={(event) => setCustomStart(event.currentTarget.value)}
|
||||
aria-label={t('usage.filter.startTime')}
|
||||
/>
|
||||
<span>{t('usage.filter.to')}</span>
|
||||
<input
|
||||
type="datetime-local"
|
||||
value={customEnd}
|
||||
onChange={(event) => setCustomEnd(event.currentTarget.value)}
|
||||
aria-label={t('usage.filter.endTime')}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</section>
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
+13
-125
@@ -98,104 +98,28 @@
|
||||
background: var(--usage-page-hover);
|
||||
}
|
||||
|
||||
/* Usage records: keep the common path quiet, and move diagnostic filters out of the way. */
|
||||
.usage-records-page .usage-filter-panel {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.usage-records-page .usage-filter-header {
|
||||
/* Keep every usage filter visible. Each select sizes to its option text. */
|
||||
.usage-records-page .usage-filter-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
min-height: 22px;
|
||||
}
|
||||
|
||||
.usage-records-page .usage-filter-title {
|
||||
color: var(--text-primary);
|
||||
font-size: var(--font-size-caption);
|
||||
font-weight: 650;
|
||||
}
|
||||
|
||||
.usage-records-page .usage-filter-active {
|
||||
padding: 2px 7px;
|
||||
border-radius: 999px;
|
||||
background: color-mix(in srgb, var(--ui-accent) 10%, transparent);
|
||||
color: var(--ui-accent-strong);
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.usage-records-page .usage-filter-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.usage-records-page .usage-filter-group.usage-filter-primary {
|
||||
display: flex;
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
flex-wrap: nowrap;
|
||||
grid-template-columns: none;
|
||||
}
|
||||
|
||||
.usage-records-page .usage-filter-primary .usage-filter-item {
|
||||
flex: 0 1 230px;
|
||||
min-width: 170px;
|
||||
}
|
||||
|
||||
.usage-records-page .usage-filter-toggle {
|
||||
display: inline-flex;
|
||||
.usage-records-page .usage-filter-item {
|
||||
flex: 0 0 auto;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
height: 36px;
|
||||
padding: 0 11px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 10px;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-secondary);
|
||||
font: inherit;
|
||||
font-size: var(--font-size-caption);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: border-color .15s ease, background .15s ease, color .15s ease;
|
||||
}
|
||||
|
||||
.usage-records-page .usage-filter-toggle:hover,
|
||||
.usage-records-page .usage-filter-toggle.is-open {
|
||||
border-color: var(--ui-accent);
|
||||
background: color-mix(in srgb, var(--ui-accent) 7%, var(--bg-primary));
|
||||
color: var(--ui-accent-strong);
|
||||
}
|
||||
|
||||
.usage-records-page .usage-filter-toggle b {
|
||||
display: inline-grid;
|
||||
min-width: 18px;
|
||||
height: 18px;
|
||||
place-items: center;
|
||||
padding: 0 5px;
|
||||
border-radius: 999px;
|
||||
background: color-mix(in srgb, var(--ui-accent) 14%, transparent);
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.usage-records-page .usage-filter-advanced {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr));
|
||||
gap: 8px;
|
||||
padding-top: 8px;
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.usage-records-page .usage-filter-advanced .usage-filter-item {
|
||||
width: max-content;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
padding: 0 10px 0 12px;
|
||||
}
|
||||
|
||||
.usage-records-page .usage-custom-range {
|
||||
padding-top: 8px;
|
||||
.usage-records-page .usage-filter-item select {
|
||||
width: max-content;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
field-sizing: content;
|
||||
padding: 0 20px 0 0;
|
||||
}
|
||||
|
||||
/* The default event view is intentionally compact; secondary diagnostics remain available via tooltips and Columns. */
|
||||
@@ -231,39 +155,3 @@
|
||||
background: color-mix(in srgb, var(--usage-log-surface) 97%, var(--ui-accent));
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.usage-records-page .usage-filter-row {
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.usage-records-page .usage-filter-group.usage-filter-primary {
|
||||
width: 100%;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.usage-records-page .usage-filter-primary .usage-filter-item {
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.usage-records-page .usage-filter-toggle {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.usage-records-page .usage-filter-advanced {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.usage-records-page .usage-filter-group.usage-filter-primary,
|
||||
.usage-records-page .usage-filter-advanced {
|
||||
grid-template-columns: 1fr;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.usage-records-page .usage-filter-advanced {
|
||||
display: grid;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,18 @@
|
||||
/* Light-mode workspace surfaces. Keep the sidebar and semantic status colors separate. */
|
||||
:root:not([data-theme='dark']) {
|
||||
--theme-faf9f5: #fff;
|
||||
--bg-secondary: #fff;
|
||||
}
|
||||
|
||||
:root:not([data-theme='dark']),
|
||||
:root:not([data-theme='dark']) body,
|
||||
:root:not([data-theme='dark']) #root,
|
||||
:root:not([data-theme='dark']) .app-shell,
|
||||
:root:not([data-theme='dark']) .workspace,
|
||||
:root:not([data-theme='dark']) .workspace .content {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
:root:not([data-theme='dark']) :is(.workspace, .config-dialog, dialog) {
|
||||
--bg-primary: #fff;
|
||||
--bg-secondary: #fff;
|
||||
@@ -27,11 +41,6 @@
|
||||
--text-quaternary: #526174;
|
||||
}
|
||||
|
||||
:root:not([data-theme='dark']) .workspace,
|
||||
:root:not([data-theme='dark']) .workspace .content {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
:root:not([data-theme='dark']) .workspace :is(
|
||||
.agent-model-section, .agent-auth-segmented, .routing-segmented,
|
||||
.plugin-tabs, .usage-analysis-metric,
|
||||
@@ -114,6 +123,64 @@
|
||||
--usage-log-hover: #fff;
|
||||
}
|
||||
|
||||
/* The usage views and outer panels should read as clean white documents in light mode.
|
||||
Remove neutral zebra fills, gray collector pills, and diffuse card shadows while
|
||||
retaining borders and semantic colors for structure. */
|
||||
:root:not([data-theme='dark']) .workspace :is(
|
||||
.usage-records-page,
|
||||
.usage-tab-panel,
|
||||
.usage-filter-panel,
|
||||
.usage-request-log,
|
||||
.usage-events-footer
|
||||
) {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
:root:not([data-theme='dark']) .workspace .usage-records-page :is(
|
||||
.usage-trend-panel,
|
||||
.usage-health-panel,
|
||||
.usage-events-panel
|
||||
) {
|
||||
background: #fff;
|
||||
border: 1px solid var(--border-color);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
:root:not([data-theme='dark']) .workspace .usage-records-page :is(
|
||||
.usage-filter-item,
|
||||
.usage-filter-toggle
|
||||
) {
|
||||
background: #fff;
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
:root:not([data-theme='dark']) .workspace .usage-records-page
|
||||
.usage-request-log .usage-events-table tbody tr td,
|
||||
:root:not([data-theme='dark']) .workspace .usage-records-page
|
||||
.usage-request-log .usage-events-table tbody tr:nth-child(even) td,
|
||||
:root:not([data-theme='dark']) .workspace .usage-records-page
|
||||
.usage-request-log .usage-events-table tbody tr:hover td {
|
||||
background: #fff !important;
|
||||
}
|
||||
|
||||
:root:not([data-theme='dark']) .workspace .usage-records-page
|
||||
.usage-pricing-table :is(th, td) {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
:root:not([data-theme='dark']) .workspace .usage-records-page
|
||||
.usage-pricing-table tbody tr:hover td {
|
||||
background: #fff !important;
|
||||
}
|
||||
|
||||
:root:not([data-theme='dark']) .workspace .usage-records-page
|
||||
.usage-collector-state,
|
||||
:root:not([data-theme='dark']) .workspace .usage-records-page
|
||||
.usage-refresh-btn {
|
||||
background: #fff;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
/* Empty tracks still need a visible outline after neutral surfaces become white. */
|
||||
:root:not([data-theme='dark']) .workspace .usage-model-bar-track {
|
||||
box-shadow: inset 0 0 0 1px var(--border-color);
|
||||
|
||||
@@ -36,6 +36,26 @@ const path = require('node:path');
|
||||
await tabs.first().focus();
|
||||
await page.keyboard.press('ArrowRight');
|
||||
assert.equal(await tabs.nth(1).getAttribute('aria-selected'), 'true');
|
||||
await page.getByRole('tab', { name: /请求明细/ }).click();
|
||||
await page.locator('.usage-events-table tbody tr').first().waitFor();
|
||||
const usageSurfaces = await page.locator('.usage-records-page .usage-events-table tbody tr').first().evaluate(row => {
|
||||
const cell = row.querySelector('td');
|
||||
const page = row.closest('.usage-records-page');
|
||||
const status = page?.querySelector('.usage-collector-state');
|
||||
return { row: cell ? getComputedStyle(cell).backgroundColor : '', status: status ? getComputedStyle(status).backgroundColor : '' };
|
||||
});
|
||||
assert.equal(usageSurfaces.row, 'rgb(255, 255, 255)', 'light usage rows stay pure white');
|
||||
assert.equal(usageSurfaces.status, 'rgb(255, 255, 255)', 'light usage status stays pure white');
|
||||
const shellSurfaces = await page.evaluate(() => {
|
||||
const shell = document.querySelector('.app-shell');
|
||||
const content = document.querySelector('.content');
|
||||
return {
|
||||
shell: shell ? getComputedStyle(shell).backgroundColor : '',
|
||||
content: content ? getComputedStyle(content).backgroundColor : '',
|
||||
};
|
||||
});
|
||||
assert.equal(shellSurfaces.shell, 'rgb(255, 255, 255)', 'light app shell stays pure white');
|
||||
assert.equal(shellSurfaces.content, 'rgb(255, 255, 255)', 'light content stays pure white');
|
||||
await page.getByRole('tab', { name: /数据管理/ }).click();
|
||||
assert.equal(await page.locator('.usage-filter-panel').count(), 0);
|
||||
|
||||
|
||||
Reference in new issue
Block a user