77 */
88
99import { computed , Signal , signal , WritableSignal } from '@angular/core' ;
10- import { GridFocus , GridFocusInputs , GridFocusCell } from './grid-focus' ;
10+ import { GridFocus , GridFocusInputs , GridFocusCell , RowCol } from './grid-focus' ;
1111
1212// Helper type for test cells, extending GridFocusCell
1313interface TestGridCell extends GridFocusCell {
@@ -23,13 +23,13 @@ type TestSetupInputs = Partial<GridFocusInputs<TestGridCell>> & {
2323 gridFocus ?: WritableSignal < GridFocus < TestGridCell > | undefined > ;
2424} ;
2525
26- function createTestCell (
26+ export function createTestCell (
2727 gridFocus : Signal < GridFocus < TestGridCell > | undefined > ,
2828 opts : { id : string ; rowspan ?: number ; colspan ?: number } ,
2929) : TestGridCell {
3030 const el = document . createElement ( 'div' ) ;
3131 spyOn ( el , 'focus' ) . and . callThrough ( ) ;
32- let coordinates : Signal < { row : number ; column : number } > = signal ( { row : - 1 , column : - 1 } ) ;
32+ let coordinates : Signal < RowCol > = signal ( { row : - 1 , col : - 1 } ) ;
3333 const cell : TestGridCell = {
3434 id : signal ( opts . id ) ,
3535 element : signal ( el as HTMLElement ) ,
@@ -39,13 +39,13 @@ function createTestCell(
3939 rowindex : signal ( - 1 ) ,
4040 colindex : signal ( - 1 ) ,
4141 } ;
42- coordinates = computed ( ( ) => gridFocus ( ) ?. getCoordinates ( cell ) ?? { row : - 1 , column : - 1 } ) ;
42+ coordinates = computed ( ( ) => gridFocus ( ) ?. getCoordinates ( cell ) ?? { row : - 1 , col : - 1 } ) ;
4343 cell . rowindex = computed ( ( ) => coordinates ( ) . row ) ;
44- cell . colindex = computed ( ( ) => coordinates ( ) . column ) ;
44+ cell . colindex = computed ( ( ) => coordinates ( ) . col ) ;
4545 return cell ;
4646}
4747
48- function createTestCells (
48+ export function createTestCells (
4949 gridFocus : Signal < GridFocus < TestGridCell > | undefined > ,
5050 numRows : number ,
5151 numCols : number ,
@@ -60,7 +60,7 @@ function createTestCells(
6060}
6161
6262// Main helper function to instantiate GridFocus and its dependencies for testing
63- function setupGridFocus ( inputs : TestSetupInputs = { } ) : {
63+ export function setupGridFocus ( inputs : TestSetupInputs = { } ) : {
6464 cells : TestGridCell [ ] [ ] ;
6565 gridFocus : GridFocus < TestGridCell > ;
6666} {
@@ -70,7 +70,7 @@ function setupGridFocus(inputs: TestSetupInputs = {}): {
7070 const gridFocus = inputs . gridFocus ?? signal < GridFocus < TestGridCell > | undefined > ( undefined ) ;
7171 const cells = inputs . cells ?? createTestCells ( gridFocus , numRows , numCols ) ;
7272
73- const activeCoords = inputs . activeCoords ?? signal ( { row : 0 , column : 0 } ) ;
73+ const activeCoords = inputs . activeCoords ?? signal ( { row : 0 , col : 0 } ) ;
7474 const focusMode = signal < 'roving' | 'activedescendant' > (
7575 inputs . focusMode ? inputs . focusMode ( ) : 'roving' ,
7676 ) ;
@@ -95,20 +95,20 @@ function setupGridFocus(inputs: TestSetupInputs = {}): {
9595
9696describe ( 'GridFocus' , ( ) => {
9797 describe ( 'Initialization' , ( ) => {
98- it ( 'should initialize with activeCell at {row: 0, column : 0} by default' , ( ) => {
98+ it ( 'should initialize with activeCell at {row: 0, col : 0} by default' , ( ) => {
9999 const { gridFocus} = setupGridFocus ( ) ;
100- expect ( gridFocus . inputs . activeCoords ( ) ) . toEqual ( { row : 0 , column : 0 } ) ;
100+ expect ( gridFocus . inputs . activeCoords ( ) ) . toEqual ( { row : 0 , col : 0 } ) ;
101101 } ) ;
102102
103103 it ( 'should compute activeCell based on activeCell' , ( ) => {
104104 const { gridFocus, cells} = setupGridFocus ( {
105- activeCoords : signal ( { row : 1 , column : 1 } ) ,
105+ activeCoords : signal ( { row : 1 , col : 1 } ) ,
106106 } ) ;
107107 expect ( gridFocus . activeCell ( ) ) . toBe ( cells [ 1 ] [ 1 ] ) ;
108108 } ) ;
109109
110110 it ( 'should compute activeCell correctly when rowspan and colspan are set' , ( ) => {
111- const activeCoords = signal ( { row : 0 , column : 0 } ) ;
111+ const activeCoords = signal ( { row : 0 , col : 0 } ) ;
112112 const gridFocusSignal = signal < GridFocus < TestGridCell > | undefined > ( undefined ) ;
113113
114114 // Visualization of this irregular grid.
@@ -130,24 +130,58 @@ describe('GridFocus', () => {
130130 gridFocus : gridFocusSignal ,
131131 } ) ;
132132
133- activeCoords . set ( { row : 0 , column : 0 } ) ;
133+ activeCoords . set ( { row : 0 , col : 0 } ) ;
134134 expect ( gridFocus . activeCell ( ) ) . toBe ( cell_0_0 ) ;
135- activeCoords . set ( { row : 0 , column : 1 } ) ;
135+ activeCoords . set ( { row : 0 , col : 1 } ) ;
136136 expect ( gridFocus . activeCell ( ) ) . toBe ( cell_0_0 ) ;
137- activeCoords . set ( { row : 1 , column : 0 } ) ;
137+ activeCoords . set ( { row : 1 , col : 0 } ) ;
138138 expect ( gridFocus . activeCell ( ) ) . toBe ( cell_0_0 ) ;
139- activeCoords . set ( { row : 1 , column : 1 } ) ;
139+ activeCoords . set ( { row : 1 , col : 1 } ) ;
140140 expect ( gridFocus . activeCell ( ) ) . toBe ( cell_0_0 ) ;
141141
142- activeCoords . set ( { row : 0 , column : 2 } ) ;
142+ activeCoords . set ( { row : 0 , col : 2 } ) ;
143143 expect ( gridFocus . activeCell ( ) ) . toBe ( cell_0_2 ) ;
144144
145- activeCoords . set ( { row : 1 , column : 2 } ) ;
145+ activeCoords . set ( { row : 1 , col : 2 } ) ;
146146 expect ( gridFocus . activeCell ( ) ) . toBe ( cell_1_2 ) ;
147147 } ) ;
148+
149+ it ( 'should compute rowCount and colCount correctly' , ( ) => {
150+ const { gridFocus} = setupGridFocus ( {
151+ numRows : 2 ,
152+ numCols : 3 ,
153+ } ) ;
154+ expect ( gridFocus . rowCount ( ) ) . toBe ( 2 ) ;
155+ expect ( gridFocus . colCount ( ) ) . toBe ( 3 ) ;
156+ } ) ;
157+
158+ it ( 'should compute rowCount and colCount correctly when rowspan and colspan are set' , ( ) => {
159+ const gridFocusSignal = signal < GridFocus < TestGridCell > | undefined > ( undefined ) ;
160+
161+ // Visualization of this irregular grid.
162+ //
163+ // +---+---+---+
164+ // | |0,2|
165+ // + 0,0 +---+
166+ // | |1,2|
167+ // +---+---+---+
168+ //
169+ const cell_0_0 = createTestCell ( gridFocusSignal , { id : `cell-0-0` , rowspan : 2 , colspan : 2 } ) ;
170+ const cell_0_2 = createTestCell ( gridFocusSignal , { id : `cell-0-2` } ) ;
171+ const cell_1_2 = createTestCell ( gridFocusSignal , { id : `cell-1-2` } ) ;
172+ const cells = signal < TestGridCell [ ] [ ] > ( [ [ cell_0_0 , cell_0_2 ] , [ cell_1_2 ] ] ) ;
173+
174+ const { gridFocus} = setupGridFocus ( {
175+ cells,
176+ gridFocus : gridFocusSignal ,
177+ } ) ;
178+
179+ expect ( gridFocus . rowCount ( ) ) . toBe ( 2 ) ;
180+ expect ( gridFocus . colCount ( ) ) . toBe ( 3 ) ;
181+ } ) ;
148182 } ) ;
149183
150- describe ( 'isGridDisabled() ' , ( ) => {
184+ describe ( 'isGridDisabled' , ( ) => {
151185 it ( 'should return true if inputs.disabled is true' , ( ) => {
152186 const { gridFocus} = setupGridFocus ( { disabled : signal ( true ) } ) ;
153187 expect ( gridFocus . isGridDisabled ( ) ) . toBeTrue ( ) ;
@@ -171,7 +205,7 @@ describe('GridFocus', () => {
171205 } ) ;
172206 } ) ;
173207
174- describe ( 'getActiveDescendant() ' , ( ) => {
208+ describe ( 'getActiveDescendant' , ( ) => {
175209 it ( 'should return undefined if focusMode is "roving"' , ( ) => {
176210 const { gridFocus} = setupGridFocus ( { focusMode : signal ( 'roving' ) } ) ;
177211 expect ( gridFocus . getActiveDescendant ( ) ) . toBeUndefined ( ) ;
@@ -188,13 +222,13 @@ describe('GridFocus', () => {
188222 it ( 'should return the activeCell id if focusMode is "activedescendant"' , ( ) => {
189223 const { gridFocus, cells} = setupGridFocus ( {
190224 focusMode : signal ( 'activedescendant' ) ,
191- activeCoords : signal ( { row : 2 , column : 2 } ) ,
225+ activeCoords : signal ( { row : 2 , col : 2 } ) ,
192226 } ) ;
193227 expect ( gridFocus . getActiveDescendant ( ) ) . toBe ( cells [ 2 ] [ 2 ] . id ( ) ) ;
194228 } ) ;
195229 } ) ;
196230
197- describe ( 'getGridTabindex() ' , ( ) => {
231+ describe ( 'getGridTabindex' , ( ) => {
198232 it ( 'should return 0 if grid is disabled' , ( ) => {
199233 const { gridFocus} = setupGridFocus ( { disabled : signal ( true ) } ) ;
200234 expect ( gridFocus . getGridTabindex ( ) ) . toBe ( 0 ) ;
@@ -211,7 +245,7 @@ describe('GridFocus', () => {
211245 } ) ;
212246 } ) ;
213247
214- describe ( 'getCellTabindex(cell) ' , ( ) => {
248+ describe ( 'getCellTabindex' , ( ) => {
215249 it ( 'should return -1 if grid is disabled' , ( ) => {
216250 const { gridFocus, cells} = setupGridFocus ( {
217251 numRows : 1 ,
@@ -247,7 +281,7 @@ describe('GridFocus', () => {
247281 } ) ;
248282 } ) ;
249283
250- describe ( 'isFocusable(cell) ' , ( ) => {
284+ describe ( 'isFocusable' , ( ) => {
251285 it ( 'should return true if cell is not disabled' , ( ) => {
252286 const { gridFocus, cells} = setupGridFocus ( {
253287 numRows : 1 ,
@@ -283,65 +317,127 @@ describe('GridFocus', () => {
283317 } ) ;
284318 } ) ;
285319
286- describe ( 'focus(cell)' , ( ) => {
320+ describe ( 'focusCoordinates' , ( ) => {
321+ it ( 'should return false and not change state if grid is disabled' , ( ) => {
322+ const activeCoords = signal ( { row : 0 , col : 0 } ) ;
323+ const { gridFocus, cells} = setupGridFocus ( {
324+ activeCoords,
325+ disabled : signal ( true ) ,
326+ } ) ;
327+
328+ const success = gridFocus . focusCoordinates ( { row : 1 , col : 0 } ) ;
329+
330+ expect ( success ) . toBeFalse ( ) ;
331+ expect ( activeCoords ( ) ) . toEqual ( { row : 0 , col : 0 } ) ;
332+ expect ( cells [ 1 ] [ 0 ] . element ( ) . focus ) . not . toHaveBeenCalled ( ) ;
333+ } ) ;
334+
335+ it ( 'should return false and not change state if cell is not focusable' , ( ) => {
336+ const activeCoords = signal ( { row : 0 , col : 0 } ) ;
337+ const { gridFocus, cells} = setupGridFocus ( { activeCoords} ) ;
338+ cells [ 1 ] [ 0 ] . disabled . set ( true ) ;
339+
340+ const success = gridFocus . focusCoordinates ( { row : 1 , col : 0 } ) ;
341+
342+ expect ( success ) . toBeFalse ( ) ;
343+ expect ( activeCoords ( ) ) . toEqual ( { row : 0 , col : 0 } ) ;
344+ expect ( cells [ 1 ] [ 0 ] . element ( ) . focus ) . not . toHaveBeenCalled ( ) ;
345+ } ) ;
346+
347+ it ( 'should focus cell, update activeCell and prevActiveCell in "roving" mode' , ( ) => {
348+ const activeCoords = signal ( { row : 0 , col : 0 } ) ;
349+ const { gridFocus, cells} = setupGridFocus ( {
350+ activeCoords,
351+ focusMode : signal ( 'roving' ) ,
352+ } ) ;
353+
354+ const success = gridFocus . focusCoordinates ( { row : 1 , col : 0 } ) ;
355+
356+ expect ( success ) . toBeTrue ( ) ;
357+ expect ( activeCoords ( ) ) . toEqual ( { row : 1 , col : 0 } ) ;
358+ expect ( cells [ 1 ] [ 0 ] . element ( ) . focus ) . toHaveBeenCalled ( ) ;
359+
360+ expect ( gridFocus . activeCell ( ) ) . toBe ( cells [ 1 ] [ 0 ] ) ;
361+ expect ( gridFocus . prevActiveCoords ( ) ) . toEqual ( { row : 0 , col : 0 } ) ;
362+ } ) ;
363+
364+ it ( 'should update activeCell and prevActiveCell but not call element.focus in "activedescendant" mode' , ( ) => {
365+ const activeCoords = signal ( { row : 0 , col : 0 } ) ;
366+ const { gridFocus, cells} = setupGridFocus ( {
367+ activeCoords,
368+ focusMode : signal ( 'activedescendant' ) ,
369+ } ) ;
370+
371+ const success = gridFocus . focusCoordinates ( { row : 1 , col : 0 } ) ;
372+
373+ expect ( success ) . toBeTrue ( ) ;
374+ expect ( activeCoords ( ) ) . toEqual ( { row : 1 , col : 0 } ) ;
375+ expect ( cells [ 1 ] [ 0 ] . element ( ) . focus ) . not . toHaveBeenCalled ( ) ;
376+
377+ expect ( gridFocus . activeCell ( ) ) . toBe ( cells [ 1 ] [ 0 ] ) ;
378+ expect ( gridFocus . prevActiveCoords ( ) ) . toEqual ( { row : 0 , col : 0 } ) ;
379+ } ) ;
380+ } ) ;
381+
382+ describe ( 'focusCell' , ( ) => {
287383 it ( 'should return false and not change state if grid is disabled' , ( ) => {
288- const activeCoords = signal ( { row : 0 , column : 0 } ) ;
384+ const activeCoords = signal ( { row : 0 , col : 0 } ) ;
289385 const { gridFocus, cells} = setupGridFocus ( {
290386 activeCoords,
291387 disabled : signal ( true ) ,
292388 } ) ;
293389
294- const success = gridFocus . focus ( { row : 1 , column : 0 } ) ;
390+ const success = gridFocus . focusCell ( cells [ 1 ] [ 0 ] ) ;
295391
296392 expect ( success ) . toBeFalse ( ) ;
297- expect ( activeCoords ( ) ) . toEqual ( { row : 0 , column : 0 } ) ;
393+ expect ( activeCoords ( ) ) . toEqual ( { row : 0 , col : 0 } ) ;
298394 expect ( cells [ 1 ] [ 0 ] . element ( ) . focus ) . not . toHaveBeenCalled ( ) ;
299395 } ) ;
300396
301397 it ( 'should return false and not change state if cell is not focusable' , ( ) => {
302- const activeCoords = signal ( { row : 0 , column : 0 } ) ;
398+ const activeCoords = signal ( { row : 0 , col : 0 } ) ;
303399 const { gridFocus, cells} = setupGridFocus ( { activeCoords} ) ;
304400 cells [ 1 ] [ 0 ] . disabled . set ( true ) ;
305401
306- const success = gridFocus . focus ( { row : 1 , column : 0 } ) ;
402+ const success = gridFocus . focusCell ( cells [ 1 ] [ 0 ] ) ;
307403
308404 expect ( success ) . toBeFalse ( ) ;
309- expect ( activeCoords ( ) ) . toEqual ( { row : 0 , column : 0 } ) ;
405+ expect ( activeCoords ( ) ) . toEqual ( { row : 0 , col : 0 } ) ;
310406 expect ( cells [ 1 ] [ 0 ] . element ( ) . focus ) . not . toHaveBeenCalled ( ) ;
311407 } ) ;
312408
313409 it ( 'should focus cell, update activeCell and prevActiveCell in "roving" mode' , ( ) => {
314- const activeCoords = signal ( { row : 0 , column : 0 } ) ;
410+ const activeCoords = signal ( { row : 0 , col : 0 } ) ;
315411 const { gridFocus, cells} = setupGridFocus ( {
316412 activeCoords,
317413 focusMode : signal ( 'roving' ) ,
318414 } ) ;
319415
320- const success = gridFocus . focus ( { row : 1 , column : 0 } ) ;
416+ const success = gridFocus . focusCell ( cells [ 1 ] [ 0 ] ) ;
321417
322418 expect ( success ) . toBeTrue ( ) ;
323- expect ( activeCoords ( ) ) . toEqual ( { row : 1 , column : 0 } ) ;
419+ expect ( activeCoords ( ) ) . toEqual ( { row : 1 , col : 0 } ) ;
324420 expect ( cells [ 1 ] [ 0 ] . element ( ) . focus ) . toHaveBeenCalled ( ) ;
325421
326422 expect ( gridFocus . activeCell ( ) ) . toBe ( cells [ 1 ] [ 0 ] ) ;
327- expect ( gridFocus . prevActiveCoords ( ) ) . toEqual ( { row : 0 , column : 0 } ) ;
423+ expect ( gridFocus . prevActiveCoords ( ) ) . toEqual ( { row : 0 , col : 0 } ) ;
328424 } ) ;
329425
330426 it ( 'should update activeCell and prevActiveCell but not call element.focus in "activedescendant" mode' , ( ) => {
331- const activeCoords = signal ( { row : 0 , column : 0 } ) ;
427+ const activeCoords = signal ( { row : 0 , col : 0 } ) ;
332428 const { gridFocus, cells} = setupGridFocus ( {
333429 activeCoords,
334430 focusMode : signal ( 'activedescendant' ) ,
335431 } ) ;
336432
337- const success = gridFocus . focus ( { row : 1 , column : 0 } ) ;
433+ const success = gridFocus . focusCell ( cells [ 1 ] [ 0 ] ) ;
338434
339435 expect ( success ) . toBeTrue ( ) ;
340- expect ( activeCoords ( ) ) . toEqual ( { row : 1 , column : 0 } ) ;
436+ expect ( activeCoords ( ) ) . toEqual ( { row : 1 , col : 0 } ) ;
341437 expect ( cells [ 1 ] [ 0 ] . element ( ) . focus ) . not . toHaveBeenCalled ( ) ;
342438
343439 expect ( gridFocus . activeCell ( ) ) . toBe ( cells [ 1 ] [ 0 ] ) ;
344- expect ( gridFocus . prevActiveCoords ( ) ) . toEqual ( { row : 0 , column : 0 } ) ;
440+ expect ( gridFocus . prevActiveCoords ( ) ) . toEqual ( { row : 0 , col : 0 } ) ;
345441 } ) ;
346442 } ) ;
347443} ) ;
0 commit comments