fix(HEL-221): 按确认样图实现情绪周期左右等高与夜间提示
桌面分析区固定 600px 并 stretch 对齐,评分构成在剩余高度内分配;夜间 tooltip 使用指定深色对比度,图表按容器尺寸重绘且不随视口拉满整页。 Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
co-authored by
Cursor
multica-agent
parent
8a5e78f022
commit
213f735f6a
+73
-6
@@ -1011,9 +1011,9 @@
|
|||||||
|
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
|
|
||||||
background: var(--r2-ink);
|
background: var(--sentiment-tooltip-bg);
|
||||||
|
|
||||||
color: var(--text-inverse);
|
color: var(--sentiment-tooltip-fg);
|
||||||
|
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
|
|
||||||
@@ -1021,6 +1021,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sentiment-chart-tooltip b {
|
.sentiment-chart-tooltip b {
|
||||||
|
color: inherit;
|
||||||
|
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1568,10 +1570,6 @@
|
|||||||
#sentimentCycleView .sentiment-component-item {
|
#sentimentCycleView .sentiment-component-item {
|
||||||
padding: 4px 0px;
|
padding: 4px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sentimentCycleView .sentiment-component-item small {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
@@ -1764,3 +1762,72 @@
|
|||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
#sentimentCycleView .redesigned-emotion-grid {
|
||||||
|
align-items: stretch;
|
||||||
|
|
||||||
|
height: var(--sentiment-analysis-height);
|
||||||
|
|
||||||
|
max-height: var(--sentiment-analysis-height);
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redesigned-sentiment-view .sentiment-analysis-main,
|
||||||
|
.redesigned-sentiment-view .sentiment-analysis-rail {
|
||||||
|
align-self: stretch;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
min-height: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redesigned-sentiment-view .sentiment-trend-panel {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
flex: 1 1 auto;
|
||||||
|
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
min-height: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redesigned-sentiment-view .sentiment-chart-shell {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
|
||||||
|
height: auto;
|
||||||
|
|
||||||
|
min-height: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redesigned-sentiment-view .sentiment-chart-shell canvas {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redesigned-sentiment-view .sentiment-cycle-summary {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redesigned-sentiment-view .sentiment-components-panel {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
flex: 1 1 auto;
|
||||||
|
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
min-height: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redesigned-sentiment-view .sentiment-component-list {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
flex: 1 1 auto;
|
||||||
|
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
justify-content: space-evenly;
|
||||||
|
|
||||||
|
min-height: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +1,39 @@
|
|||||||
let sentimentChartAnimationFrame = null;
|
let sentimentChartAnimationFrame = null;
|
||||||
|
let sentimentChartResizeObserver = null;
|
||||||
|
let sentimentChartLastSize = "";
|
||||||
|
|
||||||
window.XiaobaiPageModules.register("sentiment", ["sentimentCycleView"], {
|
window.XiaobaiPageModules.register("sentiment", ["sentimentCycleView"], {
|
||||||
bind: bindSentimentEvents,
|
bind: bindSentimentEvents,
|
||||||
enter: ["loadSentiment"],
|
enter: ["loadSentiment"],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function sentimentChartSizeKey(target) {
|
||||||
|
if (!target) return "";
|
||||||
|
const rect = target.getBoundingClientRect();
|
||||||
|
return `${Math.round(rect.width)}x${Math.round(rect.height)}x${window.devicePixelRatio || 1}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function observeSentimentTrendChart() {
|
||||||
|
const shell = document.querySelector("#sentimentCycleView .sentiment-chart-shell");
|
||||||
|
if (!shell) return;
|
||||||
|
if (!sentimentChartResizeObserver) {
|
||||||
|
sentimentChartResizeObserver = new ResizeObserver(() => {
|
||||||
|
if (sentimentChartAnimationFrame) return;
|
||||||
|
if (state.activeView !== "sentimentCycleView") return;
|
||||||
|
const rows = state.sentimentHistory?.rows;
|
||||||
|
if (!rows?.length) return;
|
||||||
|
const current = document.querySelector("#sentimentCycleView .sentiment-chart-shell");
|
||||||
|
const nextKey = sentimentChartSizeKey(current);
|
||||||
|
if (!nextKey || nextKey === sentimentChartLastSize) return;
|
||||||
|
drawSentimentTrendChart(rows, 1);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
sentimentChartResizeObserver.disconnect();
|
||||||
|
}
|
||||||
|
sentimentChartLastSize = sentimentChartSizeKey(shell);
|
||||||
|
sentimentChartResizeObserver.observe(shell);
|
||||||
|
}
|
||||||
|
|
||||||
async function loadSentimentHistory(force = false) {
|
async function loadSentimentHistory(force = false) {
|
||||||
if (!state.dashboard || state.sentimentLoading) return;
|
if (!state.dashboard || state.sentimentLoading) return;
|
||||||
const key = `${elements.tradeDate.value}:${state.sentimentRange}`;
|
const key = `${elements.tradeDate.value}:${state.sentimentRange}`;
|
||||||
@@ -126,6 +155,7 @@ function animateSentimentTrendChart(rows) {
|
|||||||
if (sentimentChartAnimationFrame) cancelAnimationFrame(sentimentChartAnimationFrame);
|
if (sentimentChartAnimationFrame) cancelAnimationFrame(sentimentChartAnimationFrame);
|
||||||
if (!motionEnabled()) {
|
if (!motionEnabled()) {
|
||||||
drawSentimentTrendChart(rows, 1);
|
drawSentimentTrendChart(rows, 1);
|
||||||
|
observeSentimentTrendChart();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const startedAt = performance.now();
|
const startedAt = performance.now();
|
||||||
@@ -135,7 +165,10 @@ function animateSentimentTrendChart(rows) {
|
|||||||
const progress = 1 - (1 - rawProgress) ** 3;
|
const progress = 1 - (1 - rawProgress) ** 3;
|
||||||
drawSentimentTrendChart(rows, progress);
|
drawSentimentTrendChart(rows, progress);
|
||||||
if (rawProgress < 1) sentimentChartAnimationFrame = requestAnimationFrame(frame);
|
if (rawProgress < 1) sentimentChartAnimationFrame = requestAnimationFrame(frame);
|
||||||
else sentimentChartAnimationFrame = null;
|
else {
|
||||||
|
sentimentChartAnimationFrame = null;
|
||||||
|
observeSentimentTrendChart();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
sentimentChartAnimationFrame = requestAnimationFrame(frame);
|
sentimentChartAnimationFrame = requestAnimationFrame(frame);
|
||||||
}
|
}
|
||||||
@@ -144,9 +177,9 @@ function drawSentimentTrendChart(rows, progress = 1) {
|
|||||||
const canvas = document.querySelector("#sentimentTrendChart");
|
const canvas = document.querySelector("#sentimentTrendChart");
|
||||||
if (!canvas || !rows.length || state.activeView !== "sentimentCycleView") return;
|
if (!canvas || !rows.length || state.activeView !== "sentimentCycleView") return;
|
||||||
const rect = canvas.getBoundingClientRect();
|
const rect = canvas.getBoundingClientRect();
|
||||||
if (!rect.width) return;
|
if (rect.width < 8 || rect.height < 8) return;
|
||||||
const width = Math.max(320, rect.width);
|
const width = rect.width;
|
||||||
const height = Math.max(220, rect.height);
|
const height = rect.height;
|
||||||
const ratio = window.devicePixelRatio || 1;
|
const ratio = window.devicePixelRatio || 1;
|
||||||
canvas.width = Math.round(width * ratio);
|
canvas.width = Math.round(width * ratio);
|
||||||
canvas.height = Math.round(height * ratio);
|
canvas.height = Math.round(height * ratio);
|
||||||
@@ -258,6 +291,7 @@ function drawSentimentTrendChart(rows, progress = 1) {
|
|||||||
const dateText = displayCompactDate(row.trade_date).slice(5);
|
const dateText = displayCompactDate(row.trade_date).slice(5);
|
||||||
context.fillText(dateText, x(index), height - padding.bottom + 10);
|
context.fillText(dateText, x(index), height - padding.bottom + 10);
|
||||||
});
|
});
|
||||||
|
sentimentChartLastSize = sentimentChartSizeKey(canvas.closest(".sentiment-chart-shell"));
|
||||||
}
|
}
|
||||||
|
|
||||||
function bindSentimentChartTooltip(rows) {
|
function bindSentimentChartTooltip(rows) {
|
||||||
|
|||||||
@@ -210,6 +210,9 @@
|
|||||||
--pool-table-max-height: calc(var(--content-height) - var(--topbar-height) - var(--page-pad-y) - var(--page-pad-y) - var(--card-gap));
|
--pool-table-max-height: calc(var(--content-height) - var(--topbar-height) - var(--page-pad-y) - var(--page-pad-y) - var(--card-gap));
|
||||||
--sentiment-history-max-height: 510px;
|
--sentiment-history-max-height: 510px;
|
||||||
--sentiment-history-min-height: 220px;
|
--sentiment-history-min-height: 220px;
|
||||||
|
--sentiment-analysis-height: 600px;
|
||||||
|
--sentiment-tooltip-bg: var(--text-primary);
|
||||||
|
--sentiment-tooltip-fg: var(--text-inverse);
|
||||||
--primary-share: 1.45fr;
|
--primary-share: 1.45fr;
|
||||||
--secondary-share: .75fr;
|
--secondary-share: .75fr;
|
||||||
--mobile-nav-height: 64px;
|
--mobile-nav-height: 64px;
|
||||||
@@ -505,6 +508,8 @@
|
|||||||
--chart-repair: #e2ad58;
|
--chart-repair: #e2ad58;
|
||||||
--chart-ma-10: #d39a45;
|
--chart-ma-10: #d39a45;
|
||||||
--chart-ma-20: #9aa5af;
|
--chart-ma-20: #9aa5af;
|
||||||
|
--sentiment-tooltip-bg: #26293e;
|
||||||
|
--sentiment-tooltip-fg: #e8eaed;
|
||||||
--on-action: #101418;
|
--on-action: #101418;
|
||||||
--warning-line: #6d5a38;
|
--warning-line: #6d5a38;
|
||||||
--warning-line-strong: #66502d;
|
--warning-line-strong: #66502d;
|
||||||
|
|||||||
@@ -47,6 +47,72 @@ function session(role = "admin", subscribed = true) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sentimentHistoryPayload(days = 20) {
|
||||||
|
const phases = ["冰点", "修复", "发酵", "高潮", "分化", "退潮"];
|
||||||
|
const rows = Array.from({ length: days }, (_, index) => {
|
||||||
|
const score = 28 + ((index * 7) % 55);
|
||||||
|
return {
|
||||||
|
trade_date: `2026-08-${String(index + 1).padStart(2, "0")}`,
|
||||||
|
score,
|
||||||
|
label: "情绪观察",
|
||||||
|
phase: phases[index % phases.length],
|
||||||
|
direction: index % 2 ? "升温" : "降温",
|
||||||
|
day_change: index % 2 ? 3.2 : -2.1,
|
||||||
|
seal_rate: 71.5,
|
||||||
|
limit_up_count: 40 + index,
|
||||||
|
first_board_count: 18,
|
||||||
|
second_board_count: 8,
|
||||||
|
three_plus_count: 4,
|
||||||
|
max_height: 5,
|
||||||
|
broken_count: 12,
|
||||||
|
limit_down_count: 3,
|
||||||
|
previous_limit_count: 38,
|
||||||
|
previous_positive_count: 22,
|
||||||
|
previous_positive_rate: 57.9,
|
||||||
|
average_previous_change: 1.2,
|
||||||
|
normalization: "固定锚点",
|
||||||
|
components: {
|
||||||
|
breadth: { label: "市场宽度", score: 55.8, weight: 20, summary: "红盘家数回升" },
|
||||||
|
limit: { label: "涨停连板", score: 79.6, weight: 25, summary: "连板生态改善" },
|
||||||
|
profit: { label: "赚钱效应", score: 67.0, weight: 30, summary: "昨日反馈尚可" },
|
||||||
|
ladder: { label: "涨幅结构", score: 81.5, weight: 15, summary: "高度仍在扩张" },
|
||||||
|
amount: { label: "成交活跃度", score: 46.1, weight: 10, summary: "量能略低于均值" },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return { available_days: days, rows };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renderSentimentFixture(page, days = 20) {
|
||||||
|
await page.evaluate((payload) => {
|
||||||
|
state.sentimentHistory = payload;
|
||||||
|
renderSentimentHistory();
|
||||||
|
}, sentimentHistoryPayload(days));
|
||||||
|
await page.locator('[data-view="sentimentCycleView"]').first().click();
|
||||||
|
await page.waitForTimeout(900);
|
||||||
|
}
|
||||||
|
|
||||||
|
function readSentimentLayout() {
|
||||||
|
const analysis = document.querySelector(".redesigned-emotion-grid").getBoundingClientRect();
|
||||||
|
const trend = document.querySelector(".sentiment-trend-panel").getBoundingClientRect();
|
||||||
|
const summary = document.querySelector(".sentiment-cycle-summary").getBoundingClientRect();
|
||||||
|
const components = document.querySelector(".sentiment-components-panel").getBoundingClientRect();
|
||||||
|
const chart = document.querySelector(".sentiment-chart-shell").getBoundingClientRect();
|
||||||
|
const detail = document.querySelector(".sentiment-detail-toolbar").getBoundingClientRect();
|
||||||
|
const table = document.querySelector(".sentiment-history-frame").getBoundingClientRect();
|
||||||
|
const small = document.querySelector(".sentiment-component-item small");
|
||||||
|
return {
|
||||||
|
topDelta: Math.abs(trend.top - summary.top),
|
||||||
|
bottomDelta: Math.abs(trend.bottom - components.bottom),
|
||||||
|
analysisHeight: analysis.height,
|
||||||
|
chartHeight: chart.height,
|
||||||
|
detailAfterAnalysis: detail.top > Math.max(trend.bottom, components.bottom) - 0.5,
|
||||||
|
tableVisible: table.top < window.innerHeight && table.bottom > detail.bottom,
|
||||||
|
smallVisible: small ? getComputedStyle(small).display !== "none" : false,
|
||||||
|
overflowX: document.documentElement.scrollWidth > document.documentElement.clientWidth + 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function waitForApplicationRuntime(page) {
|
function waitForApplicationRuntime(page) {
|
||||||
return expect(page.locator("body")).toHaveAttribute("data-runtime-ready", "true");
|
return expect(page.locator("body")).toHaveAttribute("data-runtime-ready", "true");
|
||||||
}
|
}
|
||||||
@@ -721,6 +787,7 @@ test("collapsed overview and sentiment layout keep a single current reading", as
|
|||||||
await expect(page.locator(".sentiment-stage-guide, [data-sentiment-stage]")).toHaveCount(0);
|
await expect(page.locator(".sentiment-stage-guide, [data-sentiment-stage]")).toHaveCount(0);
|
||||||
await expect(page.locator("#sentimentPhaseAdvice")).toHaveText("情绪指标继续走弱。");
|
await expect(page.locator("#sentimentPhaseAdvice")).toHaveText("情绪指标继续走弱。");
|
||||||
const alignment = await page.evaluate(() => {
|
const alignment = await page.evaluate(() => {
|
||||||
|
const analysis = document.querySelector(".redesigned-emotion-grid").getBoundingClientRect();
|
||||||
const components = document.querySelector(".sentiment-components-panel").getBoundingClientRect();
|
const components = document.querySelector(".sentiment-components-panel").getBoundingClientRect();
|
||||||
const trend = document.querySelector(".sentiment-trend-panel").getBoundingClientRect();
|
const trend = document.querySelector(".sentiment-trend-panel").getBoundingClientRect();
|
||||||
const summary = document.querySelector(".sentiment-cycle-summary").getBoundingClientRect();
|
const summary = document.querySelector(".sentiment-cycle-summary").getBoundingClientRect();
|
||||||
@@ -732,6 +799,8 @@ test("collapsed overview and sentiment layout keep a single current reading", as
|
|||||||
const statusStyle = getComputedStyle(document.querySelector(".sentiment-block .sentiment-text"));
|
const statusStyle = getComputedStyle(document.querySelector(".sentiment-block .sentiment-text"));
|
||||||
return {
|
return {
|
||||||
columnsAligned: Math.abs(trend.top - summary.top) < 1,
|
columnsAligned: Math.abs(trend.top - summary.top) < 1,
|
||||||
|
bottomsAligned: Math.abs(trend.bottom - components.bottom) < 1,
|
||||||
|
analysisHeight: analysis.height,
|
||||||
railAligned: Math.abs(summary.x - components.x) < 1 && Math.abs(summary.width - components.width) < 1 && components.top > summary.bottom,
|
railAligned: Math.abs(summary.x - components.x) < 1 && Math.abs(summary.width - components.width) < 1 && components.top > summary.bottom,
|
||||||
detailAfterAnalysis: detail.top > Math.max(trend.bottom, components.bottom),
|
detailAfterAnalysis: detail.top > Math.max(trend.bottom, components.bottom),
|
||||||
chartHeight: chart.height,
|
chartHeight: chart.height,
|
||||||
@@ -743,6 +812,8 @@ test("collapsed overview and sentiment layout keep a single current reading", as
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
expect(alignment.columnsAligned).toBe(true);
|
expect(alignment.columnsAligned).toBe(true);
|
||||||
|
expect(alignment.bottomsAligned).toBe(true);
|
||||||
|
expect(Math.abs(alignment.analysisHeight - 600)).toBeLessThanOrEqual(1);
|
||||||
expect(alignment.railAligned).toBe(true);
|
expect(alignment.railAligned).toBe(true);
|
||||||
expect(alignment.detailAfterAnalysis).toBe(true);
|
expect(alignment.detailAfterAnalysis).toBe(true);
|
||||||
expect(alignment.chartHeight).toBeGreaterThanOrEqual(340);
|
expect(alignment.chartHeight).toBeGreaterThanOrEqual(340);
|
||||||
@@ -780,6 +851,68 @@ test("collapsed overview and sentiment layout keep a single current reading", as
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("sentiment cycle keeps 600px equal-height layout across zoom viewports", async ({ page }, testInfo) => {
|
||||||
|
const shotDir = testInfo.outputPath("hel-221-shots");
|
||||||
|
await mockApplication(page, session("user", true));
|
||||||
|
const viewports = [
|
||||||
|
{ name: "zoom-100", width: 2560, height: 1440 },
|
||||||
|
{ name: "zoom-110", width: 2327, height: 1309 },
|
||||||
|
{ name: "zoom-125", width: 2048, height: 1152 },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const viewport of viewports) {
|
||||||
|
await page.setViewportSize({ width: viewport.width, height: viewport.height });
|
||||||
|
await page.goto("/index.html");
|
||||||
|
await renderSentimentFixture(page, 20);
|
||||||
|
const layout = await page.evaluate(readSentimentLayout);
|
||||||
|
expect(layout.topDelta, viewport.name).toBeLessThanOrEqual(1);
|
||||||
|
expect(layout.bottomDelta, viewport.name).toBeLessThanOrEqual(1);
|
||||||
|
expect(Math.abs(layout.analysisHeight - 600), viewport.name).toBeLessThanOrEqual(1);
|
||||||
|
expect(layout.chartHeight, viewport.name).toBeGreaterThanOrEqual(450);
|
||||||
|
expect(layout.detailAfterAnalysis, viewport.name).toBe(true);
|
||||||
|
expect(layout.tableVisible, viewport.name).toBe(true);
|
||||||
|
expect(layout.smallVisible, viewport.name).toBe(true);
|
||||||
|
expect(layout.overflowX, viewport.name).toBe(false);
|
||||||
|
await page.screenshot({
|
||||||
|
path: `${shotDir}/day-${viewport.name}.png`,
|
||||||
|
fullPage: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 2560, height: 1440 });
|
||||||
|
await page.locator("#themeToggle").click();
|
||||||
|
await expect(page.locator("html")).toHaveAttribute("data-theme", "dark");
|
||||||
|
await page.waitForTimeout(200);
|
||||||
|
const nightLayout = await page.evaluate(readSentimentLayout);
|
||||||
|
expect(nightLayout.topDelta).toBeLessThanOrEqual(1);
|
||||||
|
expect(nightLayout.bottomDelta).toBeLessThanOrEqual(1);
|
||||||
|
expect(Math.abs(nightLayout.analysisHeight - 600)).toBeLessThanOrEqual(1);
|
||||||
|
await page.locator("#sentimentTrendChart").hover({ position: { x: 280, y: 120 } });
|
||||||
|
const tooltip = page.locator("#sentimentChartTooltip");
|
||||||
|
await expect(tooltip).toBeVisible();
|
||||||
|
await expect(tooltip).toContainText("温度");
|
||||||
|
const tooltipStyle = await tooltip.evaluate((node) => {
|
||||||
|
const style = getComputedStyle(node);
|
||||||
|
const bold = getComputedStyle(node.querySelector("b") || node);
|
||||||
|
return { background: style.backgroundColor, color: style.color, bold: bold.color };
|
||||||
|
});
|
||||||
|
expect(tooltipStyle.background).toBe("rgb(38, 41, 62)");
|
||||||
|
expect(tooltipStyle.color).toBe("rgb(232, 234, 237)");
|
||||||
|
expect(tooltipStyle.bold).toBe("rgb(232, 234, 237)");
|
||||||
|
await page.screenshot({ path: `${shotDir}/night-zoom-100.png`, fullPage: true });
|
||||||
|
|
||||||
|
await page.locator("#themeToggle").click();
|
||||||
|
await expect(page.locator("html")).toHaveAttribute("data-theme", "light");
|
||||||
|
await page.locator("#sentimentTrendChart").hover({ position: { x: 280, y: 120 } });
|
||||||
|
await expect(tooltip).toBeVisible();
|
||||||
|
const lightTooltip = await tooltip.evaluate((node) => {
|
||||||
|
const style = getComputedStyle(node);
|
||||||
|
return { background: style.backgroundColor, color: style.color };
|
||||||
|
});
|
||||||
|
expect(lightTooltip.background).toBe("rgb(31, 35, 41)");
|
||||||
|
expect(lightTooltip.color).toBe("rgb(255, 255, 255)");
|
||||||
|
});
|
||||||
|
|
||||||
test("limit-up pool separates stock identity and restores the reason column", async ({ page }) => {
|
test("limit-up pool separates stock identity and restores the reason column", async ({ page }) => {
|
||||||
await mockApplication(page, session("user", true));
|
await mockApplication(page, session("user", true));
|
||||||
await page.goto("/index.html");
|
await page.goto("/index.html");
|
||||||
|
|||||||
@@ -344,6 +344,22 @@ class FrontendContractTests(unittest.TestCase):
|
|||||||
self.assertIn("max-height: var(--sentiment-history-max-height);", self.sentiment_styles)
|
self.assertIn("max-height: var(--sentiment-history-max-height);", self.sentiment_styles)
|
||||||
self.assertIn("overflow: auto;", self.sentiment_styles)
|
self.assertIn("overflow: auto;", self.sentiment_styles)
|
||||||
|
|
||||||
|
def test_sentiment_equal_height_and_tooltip_tokens(self):
|
||||||
|
self.assertIn("--sentiment-analysis-height: 600px;", self.tokens)
|
||||||
|
self.assertIn("--sentiment-tooltip-bg: var(--text-primary);", self.tokens)
|
||||||
|
self.assertIn("--sentiment-tooltip-fg: var(--text-inverse);", self.tokens)
|
||||||
|
self.assertIn("--sentiment-tooltip-bg: #26293e;", self.tokens)
|
||||||
|
self.assertIn("--sentiment-tooltip-fg: #e8eaed;", self.tokens)
|
||||||
|
self.assertIn("height: var(--sentiment-analysis-height);", self.sentiment_styles)
|
||||||
|
self.assertIn("max-height: var(--sentiment-analysis-height);", self.sentiment_styles)
|
||||||
|
self.assertIn("justify-content: space-evenly;", self.sentiment_styles)
|
||||||
|
self.assertIn("background: var(--sentiment-tooltip-bg);", self.sentiment_styles)
|
||||||
|
self.assertIn("color: var(--sentiment-tooltip-fg);", self.sentiment_styles)
|
||||||
|
self.assertIn("new ResizeObserver", self.script)
|
||||||
|
self.assertIn("#sentimentCycleView .redesigned-emotion-grid {", self.sentiment_styles)
|
||||||
|
self.assertNotIn("height: 100vh", self.sentiment_styles)
|
||||||
|
self.assertNotIn("min-height: 100%", self.sentiment_styles)
|
||||||
|
|
||||||
def test_mentor_final_visual_fix_contract(self):
|
def test_mentor_final_visual_fix_contract(self):
|
||||||
shell_styles = (STATIC_DIR / "shared" / "shell.css").read_text(encoding="utf-8")
|
shell_styles = (STATIC_DIR / "shared" / "shell.css").read_text(encoding="utf-8")
|
||||||
mentor_html = (STATIC_DIR / "pages" / "mentor" / "page.html").read_text(encoding="utf-8")
|
mentor_html = (STATIC_DIR / "pages" / "mentor" / "page.html").read_text(encoding="utf-8")
|
||||||
|
|||||||
Reference in New Issue
Block a user