Skip to content

Commit b82336a

Browse files
committed
refactor(tile): Drag events dispatch
* Added missing drag events dispatch in the tile * Cleaned-up some drag handling logic and removed some dead code * Adjusted tests
1 parent 6503b5d commit b82336a

File tree

8 files changed

+121
-112
lines changed

8 files changed

+121
-112
lines changed

src/components/common/controllers/drag.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,10 @@ class DragController implements ReactiveController {
244244
this._updatePosition(event);
245245

246246
const parameters = { event, state: this._stateParameters };
247-
this._options.start?.call(this._host, parameters);
247+
if (this._options.start?.call(this._host, parameters) === false) {
248+
this._removeGhost();
249+
return;
250+
}
248251

249252
this._assignPosition(this._dragItem);
250253
this._setDragState();

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

Lines changed: 71 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,36 @@ describe('Tile drag and drop', () => {
5454
);
5555
}
5656

57+
type DragOverOptions = {
58+
x: number;
59+
y: number;
60+
dx: number;
61+
dy: number;
62+
};
63+
64+
/**
65+
* Simulates drag over behavior for the given tile by firing two pointermove events
66+
* inside the tile's bounding box.
67+
*/
68+
function simulateTileDragOver(
69+
tile: IgcTileComponent,
70+
options?: Partial<DragOverOptions>
71+
) {
72+
const opts = Object.assign({ x: 0, y: 0, dx: 1, dy: 1 }, options);
73+
74+
simulatePointerMove(
75+
tile,
76+
{ clientX: opts.x, clientY: opts.y },
77+
{ x: opts.dx, y: opts.dy },
78+
2
79+
);
80+
}
81+
5782
async function dragAndDrop(tile: IgcTileComponent, target: IgcTileComponent) {
5883
const { x, y } = getCenterPoint(target);
5984

6085
simulatePointerDown(tile);
61-
simulatePointerMove(tile, { clientX: x, clientY: y });
86+
simulateTileDragOver(tile, { x, y });
6287

6388
await viewTransitionComplete();
6489

@@ -89,11 +114,11 @@ describe('Tile drag and drop', () => {
89114
});
90115

91116
it('should not allow dragging tiles', async () => {
92-
const eventSpy = spy(tileManager, 'emitEvent');
93-
94117
const draggedTile = getTile(0);
95118
const dropTarget = getTile(1);
96119

120+
const eventSpy = spy(draggedTile, 'emitEvent');
121+
97122
await dragAndDrop(draggedTile, dropTarget);
98123

99124
expect(eventSpy).not.called;
@@ -132,42 +157,55 @@ describe('Tile drag and drop', () => {
132157
);
133158
});
134159

135-
it('should correctly fire `igcTileDragStarted` event', async () => {
136-
const eventSpy = spy(tileManager, 'emitEvent');
137-
160+
it('should correctly fire `igcTileDragStart` event', async () => {
138161
const tile = getTile(0);
162+
const eventSpy = spy(tile, 'emitEvent');
139163

140164
simulatePointerDown(tile);
141-
await elementUpdated(tileManager);
165+
await elementUpdated(tile);
142166

143-
expect(eventSpy).calledOnceWithExactly('igcTileDragStarted', {
167+
expect(eventSpy).calledOnceWithExactly('igcTileDragStart', {
144168
detail: tile,
169+
cancelable: true,
145170
});
146171
});
147172

148-
it('should correctly fire `igcTileDragEnded` event', async () => {
149-
const eventSpy = spy(tileManager, 'emitEvent');
173+
it('should stop drag operation when `igcTileDragStart` is prevented', async () => {
174+
const [tile, target] = [getTile(0), getTile(4)];
175+
const eventSpy = spy(tile, 'emitEvent');
150176

177+
tile.addEventListener('igcTileDragStart', (event) => {
178+
event.preventDefault();
179+
});
180+
181+
await dragAndDrop(tile, target);
182+
183+
expect(eventSpy).calledOnceWith('igcTileDragStart');
184+
});
185+
186+
it('should correctly fire `igcTileDragEnd` event', async () => {
151187
const [tile, target] = [getTile(0), getTile(4)];
188+
const eventSpy = spy(tile, 'emitEvent');
152189

153190
await dragAndDrop(tile, target);
154191

155192
expect(eventSpy).calledTwice;
156-
expect(eventSpy).calledWith('igcTileDragEnded', {
193+
expect(eventSpy).calledWith('igcTileDragEnd', {
157194
detail: tile,
158195
});
159196
});
160197

161198
it('should cancel dragging with Escape', async () => {
162199
const draggedTile = getTile(0);
163200
const dropTarget = getTile(4);
201+
const eventSpy = spy(draggedTile, 'emitEvent');
164202
const { x, y } = getCenterPoint(dropTarget);
165203

166204
expect(draggedTile.position).to.equal(0);
167205
expect(dropTarget.position).to.equal(4);
168206

169207
simulatePointerDown(draggedTile);
170-
simulatePointerMove(draggedTile, { clientX: x, clientY: y });
208+
simulateTileDragOver(draggedTile, { x, y });
171209

172210
await viewTransitionComplete();
173211
expect(draggedTile.position).to.equal(4);
@@ -176,6 +214,8 @@ describe('Tile drag and drop', () => {
176214
simulateKeyboard(tileManager, escapeKey);
177215
await viewTransitionComplete();
178216

217+
expect(eventSpy).calledWith('igcTileDragCancel');
218+
179219
expect(draggedTile.position).to.equal(0);
180220
expect(dropTarget.position).to.equal(4);
181221
});
@@ -266,14 +306,12 @@ describe('Tile drag and drop', () => {
266306
const { x, y } = getCenterPoint(dropTarget);
267307

268308
simulatePointerDown(draggedTile);
269-
simulatePointerMove(draggedTile, { clientX: x, clientY: y });
309+
simulateTileDragOver(draggedTile, { x, y });
270310
await viewTransitionComplete();
271311

272312
// Simulate second dragover event (inside dropTarget bounds)
273-
simulatePointerMove(draggedTile, {
274-
clientX: x + 5,
275-
clientY: y + 5,
276-
});
313+
simulateTileDragOver(draggedTile, { x: x + 5, y: y + 5 });
314+
277315
await viewTransitionComplete();
278316

279317
expect(draggedTile.position).to.equal(1);
@@ -284,7 +322,6 @@ describe('Tile drag and drop', () => {
284322
});
285323
});
286324

287-
// REVIEW after tile header is finalized
288325
describe('Tile header drag', () => {
289326
beforeEach(async () => {
290327
tileManager = await fixture<IgcTileManagerComponent>(
@@ -300,7 +337,7 @@ describe('Tile drag and drop', () => {
300337
const { x, y } = getCenterPoint(target);
301338

302339
simulatePointerDown(header);
303-
simulatePointerMove(tile, { clientX: x, clientY: y });
340+
simulateTileDragOver(tile, { x, y });
304341

305342
await viewTransitionComplete();
306343

@@ -311,7 +348,7 @@ describe('Tile drag and drop', () => {
311348
it('should rearrange tiles when the tile is dropped', async () => {
312349
const draggedTile = getTile(3);
313350
const dropTarget = getTile(1);
314-
const eventSpy = spy(tileManager, 'emitEvent');
351+
const eventSpy = spy(draggedTile, 'emitEvent');
315352

316353
await dragAndDropFromHeader(draggedTile, dropTarget);
317354

@@ -334,14 +371,14 @@ describe('Tile drag and drop', () => {
334371
it('should not start dragging if pointer is not over the header', async () => {
335372
const draggedTile = getTile(0);
336373
const dropTarget = getTile(1);
337-
const eventSpy = spy(tileManager, 'emitEvent');
374+
const eventSpy = spy(draggedTile, 'emitEvent');
338375

339376
const contentContainer = getTileContentContainer(draggedTile);
340377
const { x, y } = getCenterPoint(dropTarget);
341378

342379
simulatePointerDown(contentContainer);
343380

344-
simulatePointerMove(draggedTile, { clientX: x, clientY: y });
381+
simulateTileDragOver(draggedTile, { x, y });
345382
await viewTransitionComplete();
346383

347384
simulateLostPointerCapture(draggedTile);
@@ -372,9 +409,9 @@ describe('Tile drag and drop', () => {
372409
}
373410

374411
it('should disable drag and drop when tile is maximized', async () => {
375-
const eventSpy = spy(tileManager, 'emitEvent');
376412
const draggedTile = getTile(0);
377413
const dropTarget = getTile(1);
414+
const eventSpy = spy(draggedTile, 'emitEvent');
378415

379416
draggedTile.maximized = true;
380417
await elementUpdated(tileManager);
@@ -387,9 +424,9 @@ describe('Tile drag and drop', () => {
387424
});
388425

389426
it('should disable drag and drop when tile is in fullscreen mode', async () => {
390-
const eventSpy = spy(tileManager, 'emitEvent');
391427
const draggedTile = getTile(0);
392428
const dropTarget = getTile(1);
429+
const eventSpy = spy(draggedTile, 'emitEvent');
393430

394431
const buttonFullscreen = draggedTile.renderRoot.querySelector(
395432
'[name="fullscreen"]'
@@ -410,7 +447,8 @@ describe('Tile drag and drop', () => {
410447

411448
await dragAndDrop(draggedTile, dropTarget);
412449

413-
expect(eventSpy).not.called;
450+
expect(eventSpy).not.calledWith('igcTileDragStart');
451+
expect(eventSpy).not.calledWith('igcTileDragEnd');
414452

415453
const expectedIdsAfterDrag = [
416454
'tile0',
@@ -437,13 +475,13 @@ describe('Tile drag and drop', () => {
437475
tileManager.dragMode = 'tile-header';
438476
const tile = getTile(0);
439477
const [maximize, _] = getActionButtons(tile);
440-
const eventSpy = spy(tileManager, 'emitEvent');
478+
const eventSpy = spy(tile, 'emitEvent');
441479

442480
maximize.click();
443481
await elementUpdated(tile);
444482

445483
expect(tile.maximized).to.be.true;
446-
expect(eventSpy).not.calledWith('igcTileDragStarted');
484+
expect(eventSpy).not.calledWith('igcTileDragStart');
447485

448486
maximize.click();
449487
await elementUpdated(tile);
@@ -455,13 +493,13 @@ describe('Tile drag and drop', () => {
455493
tileManager.dragMode = 'tile';
456494
const tile = getTile(0);
457495
const [maximize, _] = getActionButtons(tile);
458-
const eventSpy = spy(tileManager, 'emitEvent');
496+
const eventSpy = spy(tile, 'emitEvent');
459497

460498
maximize.click();
461499
await elementUpdated(tile);
462500

463501
expect(tile.maximized).to.be.true;
464-
expect(eventSpy).not.calledWith('igcTileDragStarted');
502+
expect(eventSpy).not.calledWith('igcTileDragStart');
465503

466504
maximize.click();
467505
await elementUpdated(tile);
@@ -470,8 +508,8 @@ describe('Tile drag and drop', () => {
470508
});
471509

472510
it('should not start a drag operation when interacting with the slotted tile actions in `tile-header` drag mode', async () => {
473-
const eventSpy = spy(tileManager, 'emitEvent');
474511
const { tile, button } = createSlottedActionTile();
512+
const eventSpy = spy(tile, 'emitEvent');
475513

476514
tileManager.dragMode = 'tile-header';
477515
tileManager.append(tile);
@@ -480,12 +518,12 @@ describe('Tile drag and drop', () => {
480518
button.click();
481519
await elementUpdated(tile);
482520

483-
expect(eventSpy).not.calledWith('igcTileDragStarted');
521+
expect(eventSpy).not.calledWith('igcTileDragStart');
484522
});
485523

486524
it('should not start a drag operation when interacting with the slotted tile actions in `tile` drag mode', async () => {
487-
const eventSpy = spy(tileManager, 'emitEvent');
488525
const { tile, button } = createSlottedActionTile();
526+
const eventSpy = spy(tile, 'emitEvent');
489527

490528
tileManager.dragMode = 'tile';
491529
tileManager.append(tile);
@@ -494,7 +532,7 @@ describe('Tile drag and drop', () => {
494532
button.click();
495533
await elementUpdated(tile);
496534

497-
expect(eventSpy).not.calledWith('igcTileDragStarted');
535+
expect(eventSpy).not.calledWith('igcTileDragStart');
498536
});
499537
});
500538
});

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,10 +151,7 @@ describe('Tile Manager component', () => {
151151
exportparts="trigger-side, trigger, trigger-bottom"
152152
mode="deferred"
153153
part="resize">
154-
<div
155-
part="base"
156-
style="--ig-col-span:1;--ig-row-span:1;"
157-
>
154+
<div part="base">
158155
<section part="header">
159156
<header part="title">
160157
<slot name="title"></slot>

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

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,41 +13,27 @@ import {
1313
createMutationController,
1414
} from '../common/controllers/mutation-observer.js';
1515
import { registerComponent } from '../common/definitions/register.js';
16-
import type { Constructor } from '../common/mixins/constructor.js';
17-
import { EventEmitterMixin } from '../common/mixins/event-emitter.js';
1816
import { asNumber, partNameMap } from '../common/util.js';
1917
import type { TileManagerDragMode, TileManagerResizeMode } from '../types.js';
2018
import { createTilesState } from './position.js';
2119
import { createSerializer } from './serializer.js';
2220
import { all } from './themes/container.js';
2321
import { styles as shared } from './themes/shared/tile-manager.common.css.js';
2422
import { styles } from './themes/tile-manager.base.css.js';
25-
import { DraggedTileAttribute } from './tile-util.js';
2623
import IgcTileComponent from './tile.js';
2724

28-
// REVIEW: WIP
29-
export interface IgcTileManagerComponentEventMap {
30-
igcTileDragStarted: CustomEvent<IgcTileComponent>;
31-
igcTileDragEnded: CustomEvent<IgcTileComponent>;
32-
}
33-
3425
/**
3526
* The tile manager component enables the dynamic arrangement, resizing, and interaction of tiles.
3627
*
3728
* @element igc-tile-manager
3829
*
39-
* @fires igcTileDragStarted - Fired when an owning tile begins a drag operation.
40-
* @fires igcTileDragEnded - Fired when an owning tile completes a drag operation, either by dropping onto a new position or canceling the drag.
4130
*/
4231
@themes(all)
43-
export default class IgcTileManagerComponent extends EventEmitterMixin<
44-
IgcTileManagerComponentEventMap,
45-
Constructor<LitElement>
46-
>(LitElement) {
32+
export default class IgcTileManagerComponent extends LitElement {
4733
public static readonly tagName = 'igc-tile-manager';
48-
public static styles = [styles, shared];
34+
public static override styles = [styles, shared];
4935

50-
protected static shadowRootOptions: ShadowRootInit = {
36+
public static override shadowRootOptions: ShadowRootInit = {
5137
mode: 'open',
5238
slotAssignment: 'manual',
5339
};
@@ -213,7 +199,7 @@ export default class IgcTileManagerComponent extends EventEmitterMixin<
213199

214200
createMutationController(this, {
215201
callback: this._observerCallback,
216-
filter: [`${IgcTileComponent.tagName}:not([${DraggedTileAttribute}])`],
202+
filter: [IgcTileComponent.tagName],
217203
config: {
218204
childList: true,
219205
},
@@ -247,15 +233,6 @@ export default class IgcTileManagerComponent extends EventEmitterMixin<
247233
this._tilesState.assignTiles();
248234
}
249235

250-
private _handleTileDragStart({ detail }: CustomEvent<IgcTileComponent>) {
251-
this.emitEvent('igcTileDragStarted', { detail });
252-
this._setManagerContext();
253-
}
254-
255-
private _handleTileDragEnd({ detail }: CustomEvent<IgcTileComponent>) {
256-
this.emitEvent('igcTileDragEnded', { detail });
257-
}
258-
259236
// #endregion
260237

261238
// #region Public API
@@ -283,8 +260,6 @@ export default class IgcTileManagerComponent extends EventEmitterMixin<
283260
${ref(this._grid)}
284261
style=${styleMap(this._internalStyles)}
285262
part=${parts}
286-
@tileDragStart=${this._handleTileDragStart}
287-
@tileDragEnd=${this._handleTileDragEnd}
288263
>
289264
<slot></slot>
290265
</div>

0 commit comments

Comments
 (0)