feat(web): unify guided workspace states
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import { afterEach, beforeEach, expect, test, vi } from 'vitest';
|
||||
import { MemoryRouter, useNavigate } from 'react-router-dom';
|
||||
import type { TrackPlaybackResponse } from '../../api/types';
|
||||
@@ -316,6 +316,35 @@ test('uses the shared Semi editor SideSheet for mobile track criteria and eviden
|
||||
expect(screen.getByRole('button', { name: '关闭轨迹查询与明细' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('keeps the mobile track canvas focused when the viewport crosses the responsive breakpoint', async () => {
|
||||
let mobile = false;
|
||||
let mobileListener: (() => void) | undefined;
|
||||
Object.defineProperty(window, 'matchMedia', {
|
||||
configurable: true,
|
||||
value: vi.fn((query: string) => ({
|
||||
get matches() { return mobile; },
|
||||
addEventListener: (_type: string, listener: () => void) => {
|
||||
if (query.includes('680px')) mobileListener = listener;
|
||||
},
|
||||
removeEventListener: vi.fn()
|
||||
}))
|
||||
});
|
||||
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||
const view = render(<QueryClientProvider client={client}><MemoryRouter future={ROUTER_FUTURE} initialEntries={['/tracks']}><TrackPage /></MemoryRouter></QueryClientProvider>);
|
||||
|
||||
expect(view.container.querySelector('.v2-track-page')).not.toHaveClass('is-mobile-layout', 'is-rail-collapsed');
|
||||
expect(screen.getByRole('textbox', { name: '搜索轨迹车辆' })).toBeInTheDocument();
|
||||
|
||||
await act(async () => {
|
||||
mobile = true;
|
||||
mobileListener?.();
|
||||
});
|
||||
|
||||
expect(view.container.querySelector('.v2-track-page')).toHaveClass('is-mobile-layout', 'is-rail-collapsed');
|
||||
expect(document.querySelector('.v2-track-detail-sidesheet .semi-sidesheet-animation-content_hide_bottom')).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /选择车辆/ })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('keeps one clear mobile query entry before a vehicle is selected', async () => {
|
||||
Object.defineProperty(window, 'matchMedia', { configurable: true, value: vi.fn(() => ({ matches: true, addEventListener: vi.fn(), removeEventListener: vi.fn() })) });
|
||||
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||
|
||||
@@ -357,6 +357,7 @@ export default function TrackPage() {
|
||||
const [queryCollapsed, setQueryCollapsed] = useState(() => Boolean(criteria.keyword));
|
||||
const animationRef = useRef<number>();
|
||||
const lastFrameRef = useRef(0);
|
||||
const previousMobileLayoutRef = useRef(mobileLayout);
|
||||
|
||||
const params = useMemo(() => {
|
||||
const next = new URLSearchParams({ keyword: criteria.keyword, maxPoints: '1600' });
|
||||
@@ -393,6 +394,12 @@ export default function TrackPage() {
|
||||
});
|
||||
const addressSettling = Boolean(!playing && currentAddressPoint && currentAddressPoint.key !== addressPoint?.key);
|
||||
|
||||
useEffect(() => {
|
||||
const previousMobileLayout = previousMobileLayoutRef.current;
|
||||
previousMobileLayoutRef.current = mobileLayout;
|
||||
if (previousMobileLayout === mobileLayout || track?.points.length) return;
|
||||
setRailCollapsed(mobileLayout);
|
||||
}, [mobileLayout, track?.points.length]);
|
||||
useEffect(() => { setActiveIndex(0); setPlaying(false); setFollow(true); setAddressPoint(undefined); if (track?.points.length) setRailCollapsed(window.matchMedia('(max-width: 960px)').matches); }, [track?.asOf]);
|
||||
useEffect(() => {
|
||||
if (!playing || points.length < 2) return;
|
||||
|
||||
@@ -7096,6 +7096,13 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.v2-mobile-navigation .v2-mobile-nav-item:focus-visible,
|
||||
.v2-mobile-navigation > .semi-button.v2-mobile-nav-item:focus-visible {
|
||||
outline: 2px solid rgba(18, 104, 243, .42);
|
||||
outline-offset: -3px;
|
||||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .7);
|
||||
}
|
||||
|
||||
.v2-mobile-navigation > .semi-button.v2-mobile-nav-item .semi-button-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -25294,17 +25301,26 @@
|
||||
display: grid;
|
||||
width: 100%;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
gap: 0;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e0e7f0;
|
||||
border-radius: 12px;
|
||||
background: linear-gradient(180deg, #fff 0%, #fbfcfe 100%);
|
||||
margin-top: 21px;
|
||||
box-shadow: 0 8px 24px rgba(31, 48, 70, .045);
|
||||
}
|
||||
|
||||
.v2-workspace-empty-guide-step.semi-card {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e0e7f0;
|
||||
border-radius: 10px;
|
||||
background: #fff;
|
||||
box-shadow: 0 3px 12px rgba(31, 48, 70, .04);
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.v2-workspace-empty-guide-step.semi-card + .v2-workspace-empty-guide-step.semi-card {
|
||||
border-left: 1px solid #e7edf4;
|
||||
}
|
||||
|
||||
.v2-workspace-empty-guide-step > .semi-card-body {
|
||||
@@ -25420,7 +25436,6 @@
|
||||
}
|
||||
|
||||
.v2-workspace-empty-guide-steps.semi-card-group {
|
||||
gap: 6px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user