Skip to content

Commit bd35042

Browse files
committed
test: Refactored resize controller and adjusted resize tests
1 parent 0adaea8 commit bd35042

File tree

3 files changed

+95
-83
lines changed

3 files changed

+95
-83
lines changed

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

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ type ResizeControllerConfig = {
2727
};
2828

2929
class ResizeController implements ReactiveController {
30-
private static auxiliaryEvents = [
30+
private static readonly auxiliaryEvents = [
3131
'pointermove',
3232
'lostpointercapture',
3333
] as const;
@@ -52,13 +52,14 @@ class ResizeController implements ReactiveController {
5252

5353
private _host: ReactiveControllerHost & HTMLElement;
5454
private _config: ResizeControllerConfig = {};
55-
private _id!: number;
55+
private _id = -1;
56+
private _hasPointerCapture = false;
5657

5758
private _ghost: HTMLElement | null = null;
5859
protected _initialState!: DOMRect;
5960
private _state!: DOMRect;
6061

61-
protected get _element() {
62+
protected get _element(): HTMLElement {
6263
return this._config?.ref ? this._config.ref.value! : this._host;
6364
}
6465

@@ -74,7 +75,7 @@ class ResizeController implements ReactiveController {
7475

7576
// Internal state helpers
7677

77-
private _createGhost() {
78+
private _createGhost(): void {
7879
if (this._config.mode !== 'deferred') {
7980
return;
8081
}
@@ -85,20 +86,32 @@ class ResizeController implements ReactiveController {
8586
this._host.append(this._ghost);
8687
}
8788

88-
private _disposeGhost() {
89+
private _disposeGhost(): void {
8990
if (this._ghost) {
9091
this._ghost.remove();
9192
this._ghost = null;
9293
}
9394
}
9495

95-
private _setInitialState(event: PointerEvent) {
96-
const resizableElement = this._host.querySelector('div[part~="base"]');
96+
private _setPointerCaptureState(state: boolean): void {
97+
this._hasPointerCapture = state;
98+
state
99+
? this._element.setPointerCapture(this._id)
100+
: this._element.releasePointerCapture(this._id);
97101

98-
const rect = resizableElement!.getBoundingClientRect();
99-
this._initialState = structuredClone(rect);
100-
this._state = rect;
101-
this._id = event.pointerId;
102+
for (const event of ResizeController.auxiliaryEvents) {
103+
state
104+
? this._host.addEventListener(event, this)
105+
: this._host.removeEventListener(event, this);
106+
}
107+
}
108+
109+
private _setInitialState({ pointerId }: PointerEvent): void {
110+
const resizableElement = this._host.querySelector('div[part~="base"]')!;
111+
112+
this._initialState = resizableElement.getBoundingClientRect();
113+
this._state = structuredClone(this._initialState);
114+
this._id = pointerId;
102115
}
103116

104117
private _createCallbackParams(event: PointerEvent): ResizeCallbackParams {
@@ -114,22 +127,13 @@ class ResizeController implements ReactiveController {
114127
};
115128
}
116129

117-
private _toggleSubsequentEvents(set: boolean) {
118-
const method = set
119-
? this._host.addEventListener
120-
: this._host.removeEventListener;
121-
for (const type of ResizeController.auxiliaryEvents) {
122-
method(type, this);
123-
}
124-
}
125-
126130
private _shouldSkip(event: PointerEvent): boolean {
127131
return !findElementFromEventPath((e) => e === this._element, event);
128132
}
129133

130134
// Event handlers
131135

132-
private _handlePointerDown(event: PointerEvent) {
136+
private _handlePointerDown(event: PointerEvent): void {
133137
// Non-primary buttons are ignored
134138
if (event.button) {
135139
return;
@@ -138,17 +142,13 @@ class ResizeController implements ReactiveController {
138142
if (this._config?.start) {
139143
this._setInitialState(event);
140144
this._createGhost();
141-
142-
const params = this._createCallbackParams(event);
143-
this._config.start.call(this._host, params);
144-
145-
this._element.setPointerCapture(this._id);
146-
this._toggleSubsequentEvents(true);
145+
this._config.start.call(this._host, this._createCallbackParams(event));
146+
this._setPointerCaptureState(true);
147147
}
148148
}
149149

150-
private _handlePointerMove(event: PointerEvent) {
151-
if (!this._element.hasPointerCapture(this._id)) {
150+
private _handlePointerMove(event: PointerEvent): void {
151+
if (!this._hasPointerCapture) {
152152
return;
153153
}
154154

@@ -168,7 +168,7 @@ class ResizeController implements ReactiveController {
168168
});
169169
}
170170

171-
private _handlePointerEnd(event: PointerEvent) {
171+
private _handlePointerEnd(event: PointerEvent): void {
172172
Object.assign(this._host.style, {
173173
width: `${this._state.width}px`,
174174
height: `${this._state.height}px`,
@@ -181,34 +181,37 @@ class ResizeController implements ReactiveController {
181181
this.dispose();
182182
}
183183

184-
public handleEvent(event: PointerEvent) {
184+
public handleEvent(event: PointerEvent): void {
185185
if (this._shouldSkip(event)) {
186186
return;
187187
}
188188

189189
switch (event.type) {
190190
case 'touchstart':
191-
return event.preventDefault();
191+
event.preventDefault();
192+
break;
192193

193194
case 'pointerdown':
194-
return this._handlePointerDown(event);
195+
this._handlePointerDown(event);
196+
break;
195197
case 'pointermove':
196-
return this._handlePointerMove(event);
198+
this._handlePointerMove(event);
199+
break;
197200
case 'lostpointercapture':
198-
return this._handlePointerEnd(event);
201+
this._handlePointerEnd(event);
202+
break;
199203
}
200204
}
201205

202206
// Public API
203207

204-
public setConfig(config?: ResizeControllerConfig) {
208+
public setConfig(config?: ResizeControllerConfig): void {
205209
Object.assign(this._config, config);
206210
}
207211

208-
public dispose() {
212+
public dispose(): void {
209213
this._disposeGhost();
210-
this._toggleSubsequentEvents(false);
211-
this._element.releasePointerCapture(this._id);
214+
this._setPointerCaptureState(false);
212215
}
213216

214217
public hostConnected(): void {
@@ -225,6 +228,6 @@ class ResizeController implements ReactiveController {
225228
export function addResizeController(
226229
host: ReactiveControllerHost & HTMLElement,
227230
config?: ResizeControllerConfig
228-
) {
231+
): ResizeController {
229232
return new ResizeController(host, config);
230233
}

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,15 @@ export interface IgcResizeComponentEventMap {
2424
igcResizeCancel: CustomEvent<unknown>;
2525
}
2626

27+
/* blazorSuppress */
28+
/**
29+
* @element igc-resize
30+
*/
2731
export default class IgcResizeComponent extends EventEmitterMixin<
2832
IgcResizeComponentEventMap,
2933
Constructor<LitElement>
3034
>(LitElement) {
31-
public static tagName = 'igc-resize';
35+
public static readonly tagName = 'igc-resize';
3236
public static styles = [styles, shared];
3337

3438
/* blazorSuppress */

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

Lines changed: 47 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ import {
1111
simulatePointerDown,
1212
simulatePointerMove,
1313
} from '../common/utils.spec.js';
14+
import IgcResizeComponent from './resize-element.js';
1415
import IgcTileManagerComponent from './tile-manager.js';
15-
import type IgcTileComponent from './tile.js';
16+
import IgcTileComponent from './tile.js';
1617

1718
describe('Tile resize', () => {
1819
before(() => {
@@ -22,7 +23,27 @@ describe('Tile resize', () => {
2223
let tileManager: IgcTileManagerComponent;
2324

2425
function getTiles() {
25-
return Array.from(tileManager.querySelectorAll('igc-tile'));
26+
return Array.from(tileManager.querySelectorAll(IgcTileComponent.tagName));
27+
}
28+
29+
function getTileDOM(tile: IgcTileComponent) {
30+
const root = tile.shadowRoot!;
31+
32+
return {
33+
get resizeElement() {
34+
return root.querySelector(IgcResizeComponent.tagName)!;
35+
},
36+
get resizeTrigger() {
37+
return this.resizeElement.renderRoot.querySelector<HTMLElement>(
38+
'[part="trigger"]'
39+
)!;
40+
},
41+
get resizeGhost() {
42+
return this.resizeElement.children.length < 2
43+
? null
44+
: (this.resizeElement.children.item(1) as HTMLElement);
45+
},
46+
};
2647
}
2748

2849
function getTileBaseWrapper(element: IgcTileComponent) {
@@ -58,60 +79,44 @@ describe('Tile resize', () => {
5879
tileManager = await fixture<IgcTileManagerComponent>(createTileManager());
5980
});
6081

61-
const resize = async (tile: Element, clientX: number, clientY: number) => {
62-
const resizer = tile.shadowRoot?.querySelector('igc-resize');
63-
const resizeHandle =
64-
resizer?.shadowRoot?.querySelector('[part="trigger"]');
65-
66-
simulatePointerDown(resizeHandle!);
67-
68-
await elementUpdated(resizer!);
69-
70-
simulatePointerMove(resizeHandle!, { clientX });
71-
72-
await elementUpdated(resizer!);
73-
};
74-
7582
it('should create a ghost element on resize start', async () => {
76-
const tile = first(getTiles());
77-
const resizer = tile.renderRoot.querySelector('igc-resize')!;
78-
const eventSpy = spy(resizer, 'emitEvent');
79-
const resizeHandle =
80-
resizer?.shadowRoot?.querySelector('[part="trigger"]');
81-
const resizerChildren = resizer.children;
82-
expect(resizerChildren).lengthOf(1);
83+
const tileDOM = getTileDOM(first(getTiles()));
84+
const eventSpy = spy(tileDOM.resizeElement, 'emitEvent');
8385

84-
simulatePointerDown(resizeHandle!);
85-
await elementUpdated(resizer!);
86+
expect(tileDOM.resizeGhost).to.be.null;
8687

87-
expect(resizerChildren).lengthOf(2);
88-
const ghostElement = resizerChildren[1];
88+
simulatePointerDown(tileDOM.resizeTrigger);
89+
await elementUpdated(tileDOM.resizeElement);
8990

90-
expect(ghostElement).to.not.be.null;
91+
expect(tileDOM.resizeGhost).to.exist;
9192
expect(eventSpy).calledWith('igcResizeStart');
9293
});
9394

9495
it('should update ghost element styles during pointer move - columns', async () => {
9596
const tile = first(getTiles());
96-
const resizer = tile.renderRoot.querySelector('igc-resize')!;
97-
//const resizeHandle = resizer?.shadowRoot?.querySelector('[part="trigger"]');
98-
99-
const eventSpy = spy(resizer, 'emitEvent');
97+
const tileDOM = getTileDOM(tile);
98+
const tileRect = tile.getBoundingClientRect();
99+
const eventSpy = spy(tileDOM.resizeElement, 'emitEvent');
100100

101-
const { x, width } = tile.getBoundingClientRect();
102-
resize(tile, x + width * 2, 0);
101+
simulatePointerDown(tileDOM.resizeTrigger);
102+
await elementUpdated(tileDOM.resizeElement);
103103

104-
await elementUpdated(tile);
104+
expect(eventSpy).calledWith('igcResizeStart');
105+
expect(tileDOM.resizeGhost).to.exist;
106+
expect(tileRect).to.eql(tileDOM.resizeGhost!.getBoundingClientRect());
105107

106-
const updatedGhostElement = resizer.children[1] as HTMLElement;
107-
//debugger
108+
simulatePointerMove(tileDOM.resizeTrigger, {
109+
clientX: (tileRect.x + tileRect.width) * 2,
110+
});
111+
await elementUpdated(tileDOM.resizeElement);
108112

109113
expect(eventSpy).calledWith('igcResize');
114+
// FIXME: Check event arguments, ghost dimensions etc.
115+
116+
simulateLostPointerCapture(tileDOM.resizeTrigger);
117+
await elementUpdated(tileDOM.resizeElement);
110118

111-
// Check if styles are updated, also check values
112-
expect(updatedGhostElement.style.width).to.not.be.empty;
113-
expect(updatedGhostElement.style.height).to.not.be.empty;
114-
// Check values
119+
expect(eventSpy).calledWith('igcResizeEnd');
115120
});
116121

117122
xit('should set the styles on the tile and remove the ghost element on resize end', async () => {
@@ -148,7 +153,7 @@ describe('Tile resize', () => {
148153
expect(ghostElement).to.be.null;
149154
});
150155

151-
xit('should cancel resize by pressing ESC key', async () => {
156+
it.skip('should cancel resize by pressing ESC key', async () => {
152157
const tile = first(tileManager.tiles);
153158
const { x, y, width, height } = tile.getBoundingClientRect();
154159
const resizeHandle = tile.shadowRoot!.querySelector('.resize-handle')!;

0 commit comments

Comments
 (0)