|
3 | 3 | import { describe, it, expect } from 'vitest'; |
4 | 4 | import { |
5 | 5 | deriveRecordSurface, |
| 6 | + deriveRecordFlowSurface, |
6 | 7 | countAuthorableFields, |
7 | 8 | RECORD_SURFACE_PAGE_THRESHOLD, |
| 9 | + type RecordFlow, |
8 | 10 | } from './record-surface'; |
9 | 11 |
|
10 | 12 | /** Build an object def with `n` plain text fields named f0..f(n-1). */ |
@@ -54,6 +56,65 @@ describe('deriveRecordSurface (ADR-0085 §5)', () => { |
54 | 56 | }); |
55 | 57 | }); |
56 | 58 |
|
| 59 | +describe('deriveRecordFlowSurface (#2604)', () => { |
| 60 | + const TASK_FLOWS: RecordFlow[] = ['create', 'edit', 'child-create', 'child-edit']; |
| 61 | + const heavy = objWithFields(RECORD_SURFACE_PAGE_THRESHOLD); |
| 62 | + const light = objWithFields(RECORD_SURFACE_PAGE_THRESHOLD - 1); |
| 63 | + |
| 64 | + it("view keeps the #2578 behavior verbatim: heavy → route('page'), light → overlay('drawer')", () => { |
| 65 | + expect(deriveRecordFlowSurface(heavy, 'view')).toEqual({ |
| 66 | + container: 'route', surface: 'page', size: 'auto', |
| 67 | + }); |
| 68 | + expect(deriveRecordFlowSurface(light, 'view')).toEqual({ |
| 69 | + container: 'overlay', surface: 'drawer', size: 'auto', |
| 70 | + }); |
| 71 | + }); |
| 72 | + |
| 73 | + it('task flows never route: heavy → full-screen modal overlay', () => { |
| 74 | + for (const flow of TASK_FLOWS) { |
| 75 | + expect(deriveRecordFlowSurface(heavy, flow)).toEqual({ |
| 76 | + container: 'overlay', surface: 'modal', size: 'full', |
| 77 | + }); |
| 78 | + } |
| 79 | + }); |
| 80 | + |
| 81 | + it('task flows on a light object stay a drawer overlay', () => { |
| 82 | + for (const flow of TASK_FLOWS) { |
| 83 | + expect(deriveRecordFlowSurface(light, flow)).toEqual({ |
| 84 | + container: 'overlay', surface: 'drawer', size: 'auto', |
| 85 | + }); |
| 86 | + } |
| 87 | + }); |
| 88 | + |
| 89 | + it('mobile: view routes to a page; task flows become a full-screen modal', () => { |
| 90 | + expect(deriveRecordFlowSurface(light, 'view', { viewport: 'mobile' })).toEqual({ |
| 91 | + container: 'route', surface: 'page', size: 'auto', |
| 92 | + }); |
| 93 | + for (const flow of TASK_FLOWS) { |
| 94 | + expect(deriveRecordFlowSurface(light, flow, { viewport: 'mobile' })).toEqual({ |
| 95 | + container: 'overlay', surface: 'modal', size: 'full', |
| 96 | + }); |
| 97 | + } |
| 98 | + }); |
| 99 | + |
| 100 | + it('child-* flows size to the def they are given (the child), independent of any parent', () => { |
| 101 | + // A thin child stays a drawer even though its parent (not passed) is heavy. |
| 102 | + expect(deriveRecordFlowSurface(objWithFields(3), 'child-create').surface).toBe('drawer'); |
| 103 | + // A fat child gets the full-screen modal. |
| 104 | + expect(deriveRecordFlowSurface(objWithFields(40), 'child-edit')).toEqual({ |
| 105 | + container: 'overlay', surface: 'modal', size: 'full', |
| 106 | + }); |
| 107 | + }); |
| 108 | + |
| 109 | + it('honours pageThreshold and tolerates bare/malformed input', () => { |
| 110 | + expect(deriveRecordFlowSurface(objWithFields(5), 'create', { pageThreshold: 4 }).size).toBe('full'); |
| 111 | + expect(deriveRecordFlowSurface(null, 'create')).toEqual({ |
| 112 | + container: 'overlay', surface: 'drawer', size: 'auto', |
| 113 | + }); |
| 114 | + expect(deriveRecordFlowSurface({ fields: 'nope' } as unknown, 'view').surface).toBe('drawer'); |
| 115 | + }); |
| 116 | +}); |
| 117 | + |
57 | 118 | describe('countAuthorableFields', () => { |
58 | 119 | it('counts visible non-system fields only', () => { |
59 | 120 | expect(countAuthorableFields(objWithFields(5))).toBe(5); |
|
0 commit comments