Skip to content

Commit 535e4bf

Browse files
committed
Merge remote-tracking branch 'origin/mtsvyatkova/feat-1379-tile-manager' into mtsvyatkova/feat-1379-tile-manager
2 parents cf9d4e7 + 437ba64 commit 535e4bf

File tree

4 files changed

+133
-48
lines changed

4 files changed

+133
-48
lines changed

src/components/common/utils.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ export function simulateDrop(node: Element) {
285285

286286
export function simulateDoubleClick(node: Element) {
287287
node.dispatchEvent(
288-
new MouseEvent('dblclick', { bubbles: true, composed: true })
288+
new PointerEvent('dblclick', { bubbles: true, composed: true })
289289
);
290290
}
291291

src/components/tile-manager/tile-manager.spec.ts

Lines changed: 96 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { elementUpdated, expect, fixture, html } from '@open-wc/testing';
22
import { range } from 'lit/directives/range.js';
3-
import { spy } from 'sinon';
3+
import { match, restore, spy, stub } from 'sinon';
44
import { defineComponents } from '../common/definitions/defineComponents.js';
55
import { first } from '../common/util.js';
6-
import { simulateDoubleClick } from '../common/utils.spec.js';
6+
import { simulateClick, simulateDoubleClick } from '../common/utils.spec.js';
77
import IgcTileHeaderComponent from './tile-header.js';
88
import IgcTileManagerComponent from './tile-manager.js';
99
import IgcTileComponent from './tile.js';
@@ -27,9 +27,9 @@ describe('Tile Manager component', () => {
2727
return Array.from(tileManager.querySelectorAll('igc-tile'));
2828
}
2929

30-
function getTileBaseWrapper(element: IgcTileComponent) {
31-
return element.renderRoot.querySelector<HTMLDivElement>('[part~="base"]')!;
32-
}
30+
// function getTileBaseWrapper(element: IgcTileComponent) {
31+
// return element.renderRoot.querySelector<HTMLDivElement>('[part~="base"]')!;
32+
// }
3333

3434
function createTileManager() {
3535
const result = Array.from(range(5)).map(
@@ -86,10 +86,10 @@ describe('Tile Manager component', () => {
8686
// TODO: Add an initialization test with non-defined column count and minimum dimension constraints
8787
it('is correctly initialized with its default component state', () => {
8888
// TODO: Add checks for other settings when implemented
89-
expect(tileManager.columnCount).to.equal(10);
89+
expect(tileManager.columnCount).to.equal(0);
9090
expect(tileManager.dragMode).to.equal('slide');
91-
expect(tileManager.minColumnWidth).to.equal('150px');
92-
expect(tileManager.minRowHeight).to.equal('200px');
91+
expect(tileManager.minColumnWidth).to.equal(undefined);
92+
expect(tileManager.minRowHeight).to.equal(undefined);
9393
expect(tileManager.tiles).lengthOf(2);
9494
});
9595

@@ -122,7 +122,7 @@ describe('Tile Manager component', () => {
122122
expect(tileManager).shadowDom.to.equal(
123123
`<div
124124
part="base"
125-
style="--ig-column-count:10;--ig-min-col-width:150px;--ig-min-row-height:200px;"
125+
style=""
126126
>
127127
<slot></slot>
128128
</div>`
@@ -293,45 +293,117 @@ describe('Tile Manager component', () => {
293293
});
294294

295295
describe('Tile state change behavior', () => {
296+
let tile: any;
297+
296298
beforeEach(async () => {
297299
tileManager = await fixture<IgcTileManagerComponent>(createTileManager());
300+
tile = first(tileManager.tiles);
301+
302+
// Mock `requestFullscreen`
303+
tile.requestFullscreen = stub().callsFake(() => {
304+
Object.defineProperty(document, 'fullscreenElement', {
305+
value: tile,
306+
configurable: true,
307+
});
308+
return Promise.resolve();
309+
});
310+
311+
// Mock `exitFullscreen`
312+
Object.defineProperty(document, 'exitFullscreen', {
313+
value: stub().callsFake(() => {
314+
Object.defineProperty(document, 'fullscreenElement', {
315+
value: null,
316+
configurable: true,
317+
});
318+
return Promise.resolve();
319+
}),
320+
configurable: true,
321+
});
298322
});
299323

300-
// TODO Mockup browser requestFullscreen/exitFullscreen
301-
xit('should correctly fire `igcTileFullscreen` event', async () => {
302-
const tile = first(tileManager.tiles);
303-
const tileWrapper = getTileBaseWrapper(tile);
324+
afterEach(() => {
325+
Object.defineProperty(document, 'fullscreenElement', {
326+
value: null,
327+
configurable: true,
328+
});
329+
330+
restore();
331+
});
332+
333+
it('should correctly change fullscreen state on double click', async () => {
334+
simulateDoubleClick(tile);
335+
await elementUpdated(tileManager);
336+
337+
expect(tile.requestFullscreen).to.have.been.calledOnce;
338+
expect(document.exitFullscreen).to.not.have.been.called;
339+
expect(tile.fullscreen).to.be.true;
340+
341+
simulateDoubleClick(tile);
342+
await elementUpdated(tileManager);
343+
344+
expect(document.exitFullscreen).to.have.been.calledOnce;
345+
expect(tile.fullscreen).to.be.false;
346+
});
304347

348+
it('should correctly fire `igcTileFullscreen` event', async () => {
349+
const tile = first(tileManager.tiles);
350+
const tileHeader = tile.querySelector('igc-tile-header');
351+
const fullscreenButton =
352+
tileHeader?.renderRoot.querySelectorAll('igc-icon-button')[1];
305353
const eventSpy = spy(tile, 'emitEvent');
306354

307-
simulateDoubleClick(tileWrapper);
308-
await elementUpdated(tile);
355+
simulateClick(fullscreenButton!);
356+
await elementUpdated(tileManager);
309357

310358
expect(eventSpy).calledWith('igcTileFullscreen', {
311359
detail: { tile: tile, state: true },
312360
cancelable: true,
313361
});
362+
expect(tile.fullscreen).to.be.true;
363+
364+
simulateClick(fullscreenButton!);
365+
await elementUpdated(tileManager);
314366

315-
// check if tile is fullscreen
367+
expect(eventSpy).calledWith('igcTileFullscreen', {
368+
detail: { tile: tile, state: false },
369+
cancelable: true,
370+
});
371+
expect(tile.fullscreen).to.be.false;
316372
});
317373

318-
// TODO Mockup browser requestFullscreen/exitFullscreen
319-
xit('can cancel `igcTileFullscreen` event', async () => {
320-
const tile = first(tileManager.tiles);
374+
it('can cancel `igcTileFullscreen` event', async () => {
321375
const eventSpy = spy(tile, 'emitEvent');
322376

323-
tile.addEventListener('igcTileFullscreen', (ev) => {
377+
tile.addEventListener('igcTileFullscreen', (ev: CustomEvent) => {
324378
ev.preventDefault();
325379
});
326380

327381
simulateDoubleClick(tile);
328382
await elementUpdated(tileManager);
329383

330-
expect(eventSpy).calledWith('igcTileFullscreen', {
331-
detail: { tile: tile, state: false },
332-
cancelable: true,
384+
expect(eventSpy).to.have.been.calledWith(
385+
'igcTileFullscreen',
386+
match({
387+
detail: { tile: tile, state: true },
388+
cancelable: true,
389+
})
390+
);
391+
expect(tile.fullscreen).to.be.false;
392+
expect(tile.requestFullscreen).not.to.have.been.called;
393+
});
394+
395+
it('should update fullscreen property on fullscreenchange (e.g. Esc key is pressed)', async () => {
396+
tile.fullscreen = true;
397+
398+
// Mock the browser removing fullscreen element and firing a fullscreenchange event
399+
Object.defineProperty(document, 'fullscreenElement', {
400+
configurable: true,
401+
value: null,
333402
});
334-
// check if tile is not fullscreen
403+
tile.dispatchEvent(new Event('fullscreenchange'));
404+
await elementUpdated(tileManager);
405+
406+
expect(tile.fullscreen).to.be.false;
335407
});
336408

337409
//TODO Fix test by selecting header icon and simulate click on it

src/components/tile-manager/tile.ts

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ export default class IgcTileComponent extends EventEmitterMixin<
7474
private _position = -1;
7575
private _disableDrag = false;
7676
private _fullscreen = false;
77+
private _isUserTriggered = false;
7778
private _maximized = false;
7879
private _initialPointerX: number | null = null;
7980
private _initialPointerY: number | null = null;
@@ -183,8 +184,21 @@ export default class IgcTileComponent extends EventEmitterMixin<
183184
if (this._fullscreen === value) return;
184185

185186
this._fullscreen = value;
187+
188+
if (this._isUserTriggered && !this.emitFullScreenEvent()) {
189+
this._isUserTriggered = false;
190+
this._fullscreen = !value; // Rollback state if event is canceled
191+
return;
192+
}
193+
194+
if (this._fullscreen) {
195+
this.requestFullscreen();
196+
} else if (document.fullscreenElement) {
197+
document.exitFullscreen();
198+
}
199+
200+
this._isUserTriggered = false;
186201
this._context.setValue(this, true);
187-
this.handleFullscreenRequest();
188202
}
189203

190204
public get fullscreen() {
@@ -256,7 +270,8 @@ export default class IgcTileComponent extends EventEmitterMixin<
256270
// Will probably expose that as a dynamic binding based on a property
257271
// and as a response to some UI element interaction
258272
// REVIEW: fullscreen property and a tile header action button added
259-
this.addEventListener('dblclick', this.handleDoubleClick);
273+
this.addEventListener('dblclick', this.toggleFullscreen);
274+
this.addEventListener('fullscreenchange', this.handleFullscreenChange);
260275
}
261276

262277
public override connectedCallback() {
@@ -265,8 +280,17 @@ export default class IgcTileComponent extends EventEmitterMixin<
265280
}
266281

267282
public toggleFullscreen() {
283+
this._isUserTriggered = true;
268284
this.fullscreen = !this.fullscreen;
269-
this.emitFullScreenEvent();
285+
}
286+
287+
private handleFullscreenChange() {
288+
const isFullscreen = document.fullscreenElement === this;
289+
if (!isFullscreen && this._fullscreen) {
290+
// If exited fullscreen (e.g., via ESC key), update state
291+
this._isUserTriggered = true;
292+
this.fullscreen = false;
293+
}
270294
}
271295

272296
private emitFullScreenEvent() {
@@ -276,26 +300,6 @@ export default class IgcTileComponent extends EventEmitterMixin<
276300
});
277301
}
278302

279-
private handleDoubleClick() {
280-
if (!this.emitFullScreenEvent()) {
281-
return;
282-
}
283-
284-
this.fullscreen = !this.fullscreen;
285-
}
286-
287-
private async handleFullscreenRequest() {
288-
try {
289-
if (this.fullscreen) {
290-
await this.requestFullscreen();
291-
} else {
292-
await document.exitFullscreen();
293-
}
294-
} catch {
295-
document.exitFullscreen();
296-
}
297-
}
298-
299303
private handleDragStart(e: DragEvent) {
300304
const rect = this.getBoundingClientRect();
301305
const offsetX = e.clientX - rect.left;

stories/tile-manager.stories.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,9 @@ export const FinDashboard: Story = {
362362
<igc-button @click=${disableTileResize}>
363363
Toggle Tile 2 Resizing
364364
</igc-button>
365+
<igc-button @click=${toggleFullscreen}>
366+
Toggle Tile 1 Fullscreen prop
367+
</igc-button>
365368
`,
366369
};
367370

@@ -612,6 +615,12 @@ function disableTileResize() {
612615
tileManager.tiles[1].disableResize = !tileManager.tiles[1].disableResize;
613616
}
614617

618+
function toggleFullscreen() {
619+
const tileManager =
620+
document.querySelector<IgcTileManagerComponent>('igc-tile-manager')!;
621+
tileManager.tiles[1].fullscreen = !tileManager.tiles[1].fullscreen;
622+
}
623+
615624
function cancelStateChangeEvent(e: CustomEvent) {
616625
e.preventDefault();
617626
}

0 commit comments

Comments
 (0)