@@ -2088,6 +2088,200 @@ describe('PresentationEditor', () => {
20882088 boundingSpy . mockRestore ( ) ;
20892089 } ) ;
20902090
2091+ it ( 're-emits live header/footer child editor updates and transactions' , async ( ) => {
2092+ mockIncrementalLayout . mockResolvedValueOnce ( buildLayoutResult ( ) ) ;
2093+
2094+ editor = new PresentationEditor ( {
2095+ element : container ,
2096+ documentId : 'test-doc' ,
2097+ } ) ;
2098+
2099+ await vi . waitFor ( ( ) => expect ( mockIncrementalLayout ) . toHaveBeenCalled ( ) ) ;
2100+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
2101+
2102+ const pagesHost = container . querySelector ( '.presentation-editor__pages' ) as HTMLElement ;
2103+ const mockPage = document . createElement ( 'div' ) ;
2104+ mockPage . setAttribute ( 'data-page-index' , '0' ) ;
2105+ pagesHost . appendChild ( mockPage ) ;
2106+
2107+ const viewport = container . querySelector ( '.presentation-editor__viewport' ) as HTMLElement ;
2108+ vi . spyOn ( viewport , 'getBoundingClientRect' ) . mockReturnValue ( {
2109+ left : 0 ,
2110+ top : 0 ,
2111+ width : 800 ,
2112+ height : 1000 ,
2113+ right : 800 ,
2114+ bottom : 1000 ,
2115+ x : 0 ,
2116+ y : 0 ,
2117+ toJSON : ( ) => ( { } ) ,
2118+ } as DOMRect ) ;
2119+
2120+ const updateSpy = vi . fn ( ) ;
2121+ const transactionSpy = vi . fn ( ) ;
2122+ editor . on ( 'headerFooterUpdate' , updateSpy ) ;
2123+ editor . on ( 'headerFooterTransaction' , transactionSpy ) ;
2124+
2125+ viewport . dispatchEvent ( new MouseEvent ( 'dblclick' , { bubbles : true , clientX : 120 , clientY : 50 , button : 0 } ) ) ;
2126+
2127+ await vi . waitFor ( ( ) => expect ( createdSectionEditors . length ) . toBeGreaterThan ( 0 ) ) ;
2128+ await vi . waitFor ( ( ) => expect ( editor . getActiveEditor ( ) ) . toBe ( createdSectionEditors . at ( - 1 ) ?. editor ) ) ;
2129+
2130+ const sourceEditor = editor . getActiveEditor ( ) ;
2131+ expect ( sourceEditor ) . toBeDefined ( ) ;
2132+
2133+ const transaction = { docChanged : true } ;
2134+ sourceEditor ?. emit ( 'update' , { editor : sourceEditor } ) ;
2135+ sourceEditor ?. emit ( 'transaction' , { editor : sourceEditor , transaction, duration : 9 } ) ;
2136+
2137+ expect ( updateSpy ) . toHaveBeenCalledWith (
2138+ expect . objectContaining ( {
2139+ editor : expect . any ( Object ) ,
2140+ sourceEditor,
2141+ surface : 'header' ,
2142+ headerId : 'rId-header-default' ,
2143+ sectionType : 'default' ,
2144+ } ) ,
2145+ ) ;
2146+ expect ( transactionSpy ) . toHaveBeenCalledWith (
2147+ expect . objectContaining ( {
2148+ editor : expect . any ( Object ) ,
2149+ sourceEditor,
2150+ surface : 'header' ,
2151+ headerId : 'rId-header-default' ,
2152+ sectionType : 'default' ,
2153+ transaction,
2154+ duration : 9 ,
2155+ } ) ,
2156+ ) ;
2157+ } ) ;
2158+
2159+ it ( 'stops re-emitting header/footer child editor events after exiting edit mode' , async ( ) => {
2160+ mockIncrementalLayout . mockResolvedValueOnce ( buildLayoutResult ( ) ) ;
2161+
2162+ editor = new PresentationEditor ( {
2163+ element : container ,
2164+ documentId : 'test-doc' ,
2165+ } ) ;
2166+
2167+ await vi . waitFor ( ( ) => expect ( mockIncrementalLayout ) . toHaveBeenCalled ( ) ) ;
2168+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
2169+
2170+ const pagesHost = container . querySelector ( '.presentation-editor__pages' ) as HTMLElement ;
2171+ const mockPage = document . createElement ( 'div' ) ;
2172+ mockPage . setAttribute ( 'data-page-index' , '0' ) ;
2173+ pagesHost . appendChild ( mockPage ) ;
2174+
2175+ const viewport = container . querySelector ( '.presentation-editor__viewport' ) as HTMLElement ;
2176+ vi . spyOn ( viewport , 'getBoundingClientRect' ) . mockReturnValue ( {
2177+ left : 0 ,
2178+ top : 0 ,
2179+ width : 800 ,
2180+ height : 1000 ,
2181+ right : 800 ,
2182+ bottom : 1000 ,
2183+ x : 0 ,
2184+ y : 0 ,
2185+ toJSON : ( ) => ( { } ) ,
2186+ } as DOMRect ) ;
2187+
2188+ const updateSpy = vi . fn ( ) ;
2189+ const transactionSpy = vi . fn ( ) ;
2190+ editor . on ( 'headerFooterUpdate' , updateSpy ) ;
2191+ editor . on ( 'headerFooterTransaction' , transactionSpy ) ;
2192+
2193+ viewport . dispatchEvent ( new MouseEvent ( 'dblclick' , { bubbles : true , clientX : 120 , clientY : 50 , button : 0 } ) ) ;
2194+
2195+ await vi . waitFor ( ( ) => expect ( createdSectionEditors . length ) . toBeGreaterThan ( 0 ) ) ;
2196+ await vi . waitFor ( ( ) => expect ( editor . getActiveEditor ( ) ) . toBe ( createdSectionEditors . at ( - 1 ) ?. editor ) ) ;
2197+
2198+ const sourceEditor = editor . getActiveEditor ( ) ;
2199+ const transaction = { docChanged : true } ;
2200+
2201+ sourceEditor ?. emit ( 'update' , { editor : sourceEditor } ) ;
2202+ sourceEditor ?. emit ( 'transaction' , { editor : sourceEditor , transaction, duration : 9 } ) ;
2203+
2204+ expect ( updateSpy ) . toHaveBeenCalledTimes ( 1 ) ;
2205+ expect ( transactionSpy ) . toHaveBeenCalledTimes ( 1 ) ;
2206+
2207+ container . dispatchEvent ( new KeyboardEvent ( 'keydown' , { key : 'Escape' , bubbles : true } ) ) ;
2208+ await vi . waitFor ( ( ) => expect ( editor . getActiveEditor ( ) ) . not . toBe ( sourceEditor ) ) ;
2209+
2210+ sourceEditor ?. emit ( 'update' , { editor : sourceEditor } ) ;
2211+ sourceEditor ?. emit ( 'transaction' , { editor : sourceEditor , transaction, duration : 11 } ) ;
2212+
2213+ expect ( updateSpy ) . toHaveBeenCalledTimes ( 1 ) ;
2214+ expect ( transactionSpy ) . toHaveBeenCalledTimes ( 1 ) ;
2215+ } ) ;
2216+
2217+ it ( 're-emits live footer child editor updates and transactions' , async ( ) => {
2218+ mockIncrementalLayout . mockResolvedValueOnce ( buildLayoutResult ( ) ) ;
2219+
2220+ editor = new PresentationEditor ( {
2221+ element : container ,
2222+ documentId : 'test-doc' ,
2223+ } ) ;
2224+
2225+ await vi . waitFor ( ( ) => expect ( mockIncrementalLayout ) . toHaveBeenCalled ( ) ) ;
2226+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
2227+
2228+ const pagesHost = container . querySelector ( '.presentation-editor__pages' ) as HTMLElement ;
2229+ const mockPage = document . createElement ( 'div' ) ;
2230+ mockPage . setAttribute ( 'data-page-index' , '0' ) ;
2231+ pagesHost . appendChild ( mockPage ) ;
2232+
2233+ const viewport = container . querySelector ( '.presentation-editor__viewport' ) as HTMLElement ;
2234+ vi . spyOn ( viewport , 'getBoundingClientRect' ) . mockReturnValue ( {
2235+ left : 0 ,
2236+ top : 0 ,
2237+ width : 800 ,
2238+ height : 1000 ,
2239+ right : 800 ,
2240+ bottom : 1000 ,
2241+ x : 0 ,
2242+ y : 0 ,
2243+ toJSON : ( ) => ( { } ) ,
2244+ } as DOMRect ) ;
2245+
2246+ const updateSpy = vi . fn ( ) ;
2247+ const transactionSpy = vi . fn ( ) ;
2248+ editor . on ( 'headerFooterUpdate' , updateSpy ) ;
2249+ editor . on ( 'headerFooterTransaction' , transactionSpy ) ;
2250+
2251+ viewport . dispatchEvent ( new MouseEvent ( 'dblclick' , { bubbles : true , clientX : 120 , clientY : 740 , button : 0 } ) ) ;
2252+
2253+ await vi . waitFor ( ( ) => expect ( createdSectionEditors . length ) . toBeGreaterThan ( 0 ) ) ;
2254+ await vi . waitFor ( ( ) => expect ( editor . getActiveEditor ( ) ) . toBe ( createdSectionEditors . at ( - 1 ) ?. editor ) ) ;
2255+
2256+ const sourceEditor = editor . getActiveEditor ( ) ;
2257+ expect ( sourceEditor ) . toBeDefined ( ) ;
2258+
2259+ const transaction = { docChanged : true } ;
2260+ sourceEditor ?. emit ( 'update' , { editor : sourceEditor } ) ;
2261+ sourceEditor ?. emit ( 'transaction' , { editor : sourceEditor , transaction, duration : 12 } ) ;
2262+
2263+ expect ( updateSpy ) . toHaveBeenCalledWith (
2264+ expect . objectContaining ( {
2265+ editor : expect . any ( Object ) ,
2266+ sourceEditor,
2267+ surface : 'footer' ,
2268+ headerId : 'rId-footer-default' ,
2269+ sectionType : 'default' ,
2270+ } ) ,
2271+ ) ;
2272+ expect ( transactionSpy ) . toHaveBeenCalledWith (
2273+ expect . objectContaining ( {
2274+ editor : expect . any ( Object ) ,
2275+ sourceEditor,
2276+ surface : 'footer' ,
2277+ headerId : 'rId-footer-default' ,
2278+ sectionType : 'default' ,
2279+ transaction,
2280+ duration : 12 ,
2281+ } ) ,
2282+ ) ;
2283+ } ) ;
2284+
20912285 it ( 'clears leftover footer transform when entering footer editing with non-negative minY' , async ( ) => {
20922286 mockIncrementalLayout . mockResolvedValueOnce ( buildLayoutResult ( ) ) ;
20932287
0 commit comments