mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-13 15:51:00 +00:00
fix(mobile): the checks the large-screen merge left red
- The trip's segmented control writes its fit through a ref, not into the element held in state (react-hooks/immutability). - The amount field of a payment uses the body type tier, which follows the text size setting, instead of a raw size. - The form sheet tests wait the frame the edge fade now measures on, and read the wide variant from the size class the shell writes. - The native dashes spec lists its locales through node:fs, so shared needs no vite types for it (knip). - Baselines lowered where the fixes removed warnings: no-misused-promises, no-non-null-assertion in tests, the MSheet rtl entry.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"src": {
|
||||
"@typescript-eslint/no-non-null-assertion": 375,
|
||||
"@typescript-eslint/no-misused-promises": 847,
|
||||
"@typescript-eslint/no-misused-promises": 845,
|
||||
"no-console": 88,
|
||||
"complexity": 218,
|
||||
"@typescript-eslint/no-explicit-any": 144,
|
||||
@@ -35,7 +35,7 @@
|
||||
},
|
||||
"tests": {
|
||||
"@typescript-eslint/no-explicit-any": 489,
|
||||
"@typescript-eslint/no-non-null-assertion": 1912,
|
||||
"@typescript-eslint/no-non-null-assertion": 1911,
|
||||
"@typescript-eslint/no-misused-promises": 22,
|
||||
"@typescript-eslint/no-unused-vars": 51,
|
||||
"@typescript-eslint/no-floating-promises": 33,
|
||||
|
||||
@@ -79,7 +79,6 @@
|
||||
"hooks/useAnchoredPosition.ts": 3,
|
||||
"hooks/useElementRect.ts": 3,
|
||||
"mobile/components/MSegmented.tsx": 1,
|
||||
"mobile/components/MSheet.tsx": 1,
|
||||
"mobile/screens/journey/MJourneyDetail.tsx": 3,
|
||||
"mobile/screens/journey/MJourneyEntrySheet.tsx": 1,
|
||||
"mobile/screens/trip/map/MMapArea.tsx": 3,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ChevronLeft, ChevronRight, FolderSync, List, Plus, Rows3 } from 'lucide-react';
|
||||
import { useLayoutEffect, useState, type ReactNode } from 'react';
|
||||
import { useCallback, useLayoutEffect, useRef, useState, type ReactNode } from 'react';
|
||||
import { canManageDocSync } from '../../../components/Files/docsync/useDocSync';
|
||||
import { useDocSyncOffered } from '../../../components/Files/docsync/useDocSyncOffered';
|
||||
import { useIsWide } from '../../../hooks/sizeClass';
|
||||
@@ -26,14 +26,21 @@ import { collabBoardFor, collabSideBySide } from './tabs/collabPanes';
|
||||
export function GlassSegment({ children, activeKey }: { children: ReactNode; activeKey?: string }) {
|
||||
const [region, setRegion] = useState<HTMLDivElement | null>(null);
|
||||
const [track, setTrack] = useState<HTMLDivElement | null>(null);
|
||||
// The element is also kept in a ref: the fit below writes to it, and state is not to be written.
|
||||
const trackRef = useRef<HTMLDivElement | null>(null);
|
||||
const attachTrack = useCallback((el: HTMLDivElement | null) => {
|
||||
trackRef.current = el;
|
||||
setTrack(el);
|
||||
}, []);
|
||||
const fade = useEdgeFade(track);
|
||||
useLayoutEffect(() => {
|
||||
if (!region || !track) return;
|
||||
const el = trackRef.current;
|
||||
if (!region || !el) return;
|
||||
const fit = () => {
|
||||
track.dataset.tight = 'false';
|
||||
track.dataset.tight = track.scrollWidth > track.clientWidth + 0.5 ? 'true' : 'false';
|
||||
const active = track.querySelector<HTMLElement>('[aria-pressed="true"]');
|
||||
if (active && track.scrollWidth > track.clientWidth + 0.5) {
|
||||
el.dataset.tight = 'false';
|
||||
el.dataset.tight = el.scrollWidth > el.clientWidth + 0.5 ? 'true' : 'false';
|
||||
const active = el.querySelector<HTMLElement>('[aria-pressed="true"]');
|
||||
if (active && el.scrollWidth > el.clientWidth + 0.5) {
|
||||
// 'nearest' on both axes: the shell root is fixed, anything else scrolls the page behind it.
|
||||
active.scrollIntoView({ block: 'nearest', inline: 'nearest' });
|
||||
}
|
||||
@@ -49,7 +56,7 @@ export function GlassSegment({ children, activeKey }: { children: ReactNode; act
|
||||
className="pointer-events-none absolute inset-y-0 end-12 start-12 flex items-center justify-center"
|
||||
>
|
||||
<div className="pointer-events-auto flex min-w-0 max-w-full rounded-full border border-[color:var(--m-gbr)] bg-[color:var(--m-glass)] p-[3px] backdrop-blur-[24px] backdrop-saturate-[1.7]">
|
||||
<div ref={setTrack} className={`m-seg flex min-w-0 overflow-x-auto rounded-full ${edgeFadeClass(fade)}`}>
|
||||
<div ref={attachTrack} className={`m-seg flex min-w-0 overflow-x-auto rounded-full ${edgeFadeClass(fade)}`}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -182,7 +182,7 @@ export function AddPaymentSheet({
|
||||
onChange={(e) => setAmount(e.target.value)}
|
||||
placeholder={zeroAmountInput(currency)}
|
||||
aria-label={t('costs.amount')}
|
||||
className="min-w-0 flex-1 border-0 bg-transparent p-0 text-[0.84375rem] font-semibold text-m-ink outline-none [font-variant-numeric:tabular-nums] placeholder:font-normal placeholder:text-m-faint"
|
||||
className="min-w-0 flex-1 border-0 bg-transparent p-0 text-body font-semibold text-m-ink outline-none [font-variant-numeric:tabular-nums] placeholder:font-normal placeholder:text-m-faint"
|
||||
/>
|
||||
<span className="flex-none font-geist text-caption font-bold text-m-faint">{currency}</span>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { readFileSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
import { useRef, useState } from 'react';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { sizeClassOf } from '../../../src/hooks/sizeClass';
|
||||
import { useAutoGrow } from '../../../src/hooks/useAutoGrow';
|
||||
import MSheet from '../../../src/mobile/components/MSheet';
|
||||
import { edgeFadeClass, useEdgeFade } from '../../../src/mobile/components/useEdgeFade';
|
||||
@@ -115,7 +116,8 @@ describe('useEdgeFade on the vertical axis', () => {
|
||||
Object.defineProperty(el, 'scrollHeight', { configurable: true, value: 800 });
|
||||
await act(async () => {
|
||||
el.appendChild(document.createElement('div'));
|
||||
await Promise.resolve();
|
||||
// Measured on the next frame, so a burst of changes is measured once.
|
||||
await new Promise((resolve) => setTimeout(resolve, 40));
|
||||
});
|
||||
expect(result.current.end).toBe(true);
|
||||
});
|
||||
@@ -162,7 +164,12 @@ describe('form sheet stylesheet', () => {
|
||||
|
||||
it('FE-MOB-FORMSHEET-010: the short-window variant is the other side of the wide size class height bound', () => {
|
||||
expect(tailwind).toContain("addVariant('short', '@media (max-height: 500px)')");
|
||||
expect(tailwind).toContain("addVariant('wide', '@media (min-width: 600px) and (min-height: 501px)')");
|
||||
// `wide:` follows the size class the shell writes (data-size), whose height bound starts at 501.
|
||||
expect(tailwind).toContain(
|
||||
`addVariant('wide', ':where(html:has(.m-shell[data-size="wide"], .m-shell[data-size="split"])) &')`
|
||||
);
|
||||
expect(sizeClassOf(1024, 500)).toBe('compact');
|
||||
expect(sizeClassOf(1024, 501)).not.toBe('compact');
|
||||
});
|
||||
|
||||
it('FE-MOB-FORMSHEET-011: on a short window the actions sit beside the title and the fields take the rest', () => {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
/// <reference types="vite/client" />
|
||||
/// <reference types="node" />
|
||||
import type { TranslationStrings } from './types';
|
||||
|
||||
import { existsSync, readdirSync } from 'node:fs';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
/**
|
||||
@@ -11,7 +14,18 @@ import { describe, expect, it } from 'vitest';
|
||||
* reads, and the style is held here because a re-translation can bring a dash
|
||||
* back without any other check noticing.
|
||||
*/
|
||||
const nativeModules = import.meta.glob<{ default: TranslationStrings }>('./*/native.ts', { eager: true });
|
||||
const here = dirname(fileURLToPath(import.meta.url));
|
||||
const locales = readdirSync(here, { withFileTypes: true })
|
||||
.filter((entry) => entry.isDirectory() && existsSync(join(here, entry.name, 'native.ts')))
|
||||
.map((entry) => entry.name);
|
||||
const nativeModules: Record<string, { default: TranslationStrings }> = Object.fromEntries(
|
||||
await Promise.all(
|
||||
locales.map(async (locale) => [
|
||||
`./${locale}/native.ts`,
|
||||
(await import(`./${locale}/native.ts`)) as { default: TranslationStrings },
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
const DASH = /[‒–—―]| - /;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user