@sankhyalabs/core 5.20.0-dev.14 → 5.20.0-dev.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,10 @@
1
+ import { OverflowDirection } from './OverflowWatcher/types/overflow-direction.js';
2
+
3
+ export function calcMarginSize(el:Element, scrollDirection: OverflowDirection){
4
+ const computedStyle = getComputedStyle(el);
5
+ if(OverflowDirection.HORIZONTAL === scrollDirection){
6
+ return (parseInt(computedStyle.marginLeft || '0') + parseInt(computedStyle.marginRight || '0'));
7
+ }
8
+
9
+ return (parseInt(computedStyle.marginTop || '0') + parseInt(computedStyle.marginBottom || '0'));
10
+ }
@@ -1,151 +1,243 @@
1
1
  import { JSUtils } from "../JSUtils.js";
2
2
  import { OverflowDirection } from "./types/overflow-direction.js"
3
3
  import { OnOverflowCallBack } from "./types/overflow-callback.js"
4
+ import { calcMarginSize } from '../ElementUtils.js';
4
5
 
5
6
  export * from "./types/overflow-direction.js";
6
7
  export * from "./types/overflow-callback.js";
7
8
 
9
+ export const OVERFLOWED_CLASS_NAME = 'overflowed';
10
+
8
11
  export default class OverflowWatcher {
9
12
  private _onResize:OnOverflowCallBack;
10
13
  private _resizeObserver:ResizeObserver;
11
- private _lastContentRect:DOMRectReadOnly|undefined = undefined;
14
+ private _lastContainerSize:number|undefined = undefined;
15
+ private _lastContainerInstance: HTMLElement | undefined = undefined;
12
16
  private _scrollDirection = OverflowDirection.HORIZONTAL;
13
17
  private _propSize:string;
14
- private _hiddenItems:Element[] = [];
18
+ private _hiddenItemsProps:Map<Element, SizeProps> = new Map();
19
+ private _notOverFlowPros:Map<string, SizeProps> = new Map();
15
20
  private _deltaSize:number;
21
+ private _notOverFlow: string[] = [];
22
+
23
+ readonly DATA_ELEMENT_ID = 'data-element-id';
16
24
 
17
25
  /**
18
- * Cria uma instancia do OverflowWatcher
19
- *
26
+ * Cria uma instancia do OverflowWatcher
27
+ *
20
28
  * @param element - Elemento HTML que o overflow será observado.
21
29
  * @param callback - Função que sera chamada quando ocorrer overflow no elemento.
22
30
  * @param overFlowDirection - Indica direção que o overflow será monitorado.
23
31
  * @param deltaSize - Variação de tamanho que será considerada como overflow.
24
- */
25
- constructor(element:HTMLElement,
26
- callback:OnOverflowCallBack,
27
- overFlowDirection:OverflowDirection = OverflowDirection.HORIZONTAL,
28
- deltaSize:number = 10){
32
+ * @param debounce - Tempo até execução do callback em milissegundos.
33
+ * @param notOverFlow - Lista de ids ou data-element-ids dos elementos que não devem sofrer overFlow.
34
+ */
35
+ constructor({
36
+ element,
37
+ callback,
38
+ overFlowDirection = OverflowDirection.HORIZONTAL,
39
+ debounce = 200,
40
+ deltaSize = 0,
41
+ notOverFlow = []
42
+ }: OverFlowWatcherParams){
29
43
  this._onResize = callback;
30
44
  this._scrollDirection = overFlowDirection;
31
- this._resizeObserver = new ResizeObserver(JSUtils.debounce((entries: ResizeObserverEntry[]) => this.handleResize(entries), 200));
32
- this._resizeObserver.observe(element);
33
- this._propSize = this.getPropSizeByDirection();
34
- this._deltaSize = deltaSize;
45
+ this._propSize = (OverflowDirection.HORIZONTAL === overFlowDirection) ? "width" : "height";
46
+ this._resizeObserver = new ResizeObserver(JSUtils.debounce((entries: ResizeObserverEntry[]) => this.handleResize(entries), debounce));
47
+ this._resizeObserver.observe(element);
48
+ this._deltaSize = deltaSize;
49
+ this._notOverFlow = notOverFlow;
50
+ }
51
+
52
+ public addNotOverFlowElement(elementId: string){
53
+ if(!this._notOverFlow.includes(elementId)){
54
+ this._notOverFlow.push(elementId);
55
+ }
35
56
  }
36
57
 
37
58
  public destroy(){
38
59
  this._resizeObserver.disconnect();
39
60
  }
40
61
 
62
+ public forceUpdate(){
63
+ if(this._lastContainerSize && this._lastContainerInstance){
64
+ this.updateOverFlowedItems(this._lastContainerInstance, this._lastContainerSize);
65
+ }
66
+ }
67
+
41
68
  private handleResize(entries: ResizeObserverEntry[]){
69
+ if(!entries || entries.length === 0) return;
42
70
 
43
- if(!entries || entries.length === 0){
44
- return;
71
+ const container = entries[0];
72
+ const containerSize:number = (container.contentRect as any)[this._propSize];
73
+ if(!containerSize) return;
74
+
75
+ if(this.hasChangedSize(containerSize)){
76
+ this.updateOverFlowedItems(container.target as HTMLElement, containerSize);
45
77
  }
78
+ }
46
79
 
47
- const resizeItem = entries[0];
48
- const contentRect:any = resizeItem.contentRect;
80
+ private updateOverFlowedItems(container: HTMLElement, containerSize: number){
81
+ const children:Element[] = Array.from(container.children);
82
+ this.registerNotOverflowProps(children);
83
+ this.proccessElements(containerSize, children);
84
+ this._lastContainerSize = containerSize;
85
+ this._lastContainerInstance = container;
86
+ }
49
87
 
50
- if(this.isChangedSize(contentRect)){
51
- const children:Element[] = Array.from(resizeItem.target.children);
52
- this.proccessElements(contentRect[this._propSize], children);
53
- this._lastContentRect = contentRect;
54
- }
88
+ private registerNotOverflowProps(children: Element[]) {
89
+ children.forEach(childElement => {
90
+ const id = childElement.id || this.getDataElementId(childElement);
91
+ if (this.canNotRegisterNotOverFlow(id)) return;
92
+ this._notOverFlowPros.set(id, this.getElementSizeProps(childElement));
93
+ });
55
94
  }
56
95
 
57
- private isChangedSize(newContentRect:DOMRectReadOnly):boolean{
58
- if(this._lastContentRect == undefined){
59
- return true;
60
- }
61
- return Math.abs((newContentRect as any)[this._propSize] - (this._lastContentRect as any)[this._propSize]) >= this._deltaSize;
96
+ private canNotRegisterNotOverFlow(id: string) {
97
+ return !id || !this._notOverFlow.includes(id) || this._notOverFlowPros.has(id);
62
98
  }
63
99
 
100
+ private hasChangedSize(elementSize: number):boolean{
101
+ if(!this._lastContainerSize) return true;
102
+ const variation = elementSize - this._lastContainerSize;
64
103
 
65
- private getPropSizeByDirection():string{
66
- if(OverflowDirection.HORIZONTAL === this._scrollDirection){
67
- return "width"
104
+ if(variation < 0){
105
+ const absoluteVariation = Math.abs(variation);
106
+ return (absoluteVariation > this._deltaSize);
68
107
  }
69
108
 
70
- return "height";
109
+ return variation > 0;
71
110
  }
72
111
 
73
112
  private proccessElements(elementSize:number, children:Element[]){
74
-
75
- if(children.length === 0){
76
- return;
77
- }
113
+ if(children.length === 0) return;
78
114
 
79
115
  const childrenSize = this.calcChildrenSize(children);
80
-
81
116
  let diff = Number((elementSize - childrenSize).toFixed(4));
82
-
117
+
83
118
  if(diff > 0){
84
- this.proccessElementsWithoutOverFlow(diff);
119
+ this.clearOverFlow();
85
120
  return;
86
121
  }
87
122
 
88
- this.proccessElementsOverFlow(children, diff);
123
+ this.proccessElementsOverFlow(children, elementSize);
124
+ }
125
+
126
+ private clearOverFlow(){
127
+ this._hiddenItemsProps = new Map();
128
+ this._onResize([]);
89
129
  }
90
130
 
91
- private proccessElementsWithoutOverFlow(diff:number){
92
- this._hiddenItems.forEach((item:any) => {
93
-
94
- if(item.getBoundingClientRect().width < diff){
95
- this._hiddenItems.pop();
131
+ private proccessElementsOverFlow(allElements:Element[], avaliableSize:number){
132
+ const elementsThatFit: Element[] = [];
133
+ const avaliableSizeConsideringDelta = (avaliableSize - this._deltaSize);
134
+
135
+ let sumElementsSize = 0;
136
+ for (const element of allElements) {
137
+ sumElementsSize += this.calcElementSize(element);
138
+ if(this.exceedsAvaliableSize(sumElementsSize, elementsThatFit, avaliableSizeConsideringDelta)) break;
139
+ elementsThatFit.push(element);
140
+ }
141
+
142
+ const overFlowedElements = allElements.filter(element => this.isElementOverFlowing(elementsThatFit, element));
143
+
144
+ overFlowedElements.forEach(overFlowed => {
145
+ if(!this._hiddenItemsProps.has(overFlowed)){
146
+ this.registerElementSize(overFlowed);
96
147
  }
97
-
98
148
  });
99
149
 
100
- this._onResize(this._hiddenItems);
150
+ this._onResize(overFlowedElements);
101
151
  }
102
152
 
103
- private proccessElementsOverFlow(children:Element[], diff:number){
104
- const elementsOverflow = [];
105
- let sumRemovedItems = 0;
153
+ private isElementOverFlowing(elementsThatFit: Element[], element: Element) {
154
+ return !elementsThatFit.includes(element) && this.canOverFlowElement(element);
155
+ }
106
156
 
107
- while(elementsOverflow.length < children.length && sumRemovedItems < (diff * -1) ) {
108
- const itemToRemove = children.pop();
109
-
110
- if(itemToRemove != undefined && (itemToRemove as any).style.display === 'none'){
111
- continue;
112
- }
157
+ private canOverFlowElement(element: Element) {
158
+ return !this._notOverFlow.includes(element.id)
159
+ && !this._notOverFlow.includes(this.getDataElementId(element));
160
+ }
113
161
 
114
- if(itemToRemove != undefined){
115
- elementsOverflow.push(itemToRemove);
116
- sumRemovedItems += (itemToRemove.getBoundingClientRect() as any)[this._propSize];
117
- }
118
- }
162
+ private getDataElementId(element: Element): string {
163
+ return (element as HTMLElement).getAttribute('data-element-id') ?? "";
164
+ }
119
165
 
166
+ private exceedsAvaliableSize(sumElementsSize: number, elements: Element[], avaliableSize: number): boolean {
167
+ if(!this._notOverFlow.length) return sumElementsSize > avaliableSize
120
168
 
121
- this._hiddenItems = elementsOverflow;
122
- this._onResize(elementsOverflow);
169
+ const elementIdsToCalculate = this.canNotOverFlowNotIncludedIds(elements);
170
+ if(!elementIdsToCalculate.length) return sumElementsSize > avaliableSize
123
171
 
172
+ const variation = this.calculateVariation(elementIdsToCalculate);
173
+ const occupiedSize = sumElementsSize + variation;
174
+ return occupiedSize > avaliableSize;
124
175
  }
125
176
 
126
- private calcChildrenSize(children:Element[]):number{
127
- let sumChildren = 0;
128
-
129
- Array.from(children).forEach(el => {
130
- sumChildren += (el.getBoundingClientRect() as any)[this._propSize];
131
- sumChildren += this.calcMarginSize(el);
177
+ private calculateVariation(elementIdsToCalculate: string[]) {
178
+ let variation = 0
179
+ elementIdsToCalculate.forEach(id => {
180
+ const sizeProps = this._notOverFlowPros.get(id);
181
+ variation += sizeProps?.size ?? 0;
182
+ variation += sizeProps?.margin ?? 0;
132
183
  });
184
+ return variation;
185
+ }
133
186
 
134
- if(sumChildren > 0){
135
- sumChildren += this._deltaSize;
136
- }
187
+ private canNotOverFlowNotIncludedIds(elements: Element[]): string[]{
188
+ const elementsIdList = elements.map(el => el.id || this.getDataElementId(el)).filter(id => !!id);
189
+ return this._notOverFlow.filter(id => !elementsIdList.includes(id));
190
+ }
137
191
 
138
- return sumChildren;
192
+ private registerElementSize(element: Element) {
193
+ const sizeProps = this.getElementSizeProps(element);
194
+ this._hiddenItemsProps.set(element, sizeProps);
139
195
  }
140
196
 
141
- private calcMarginSize(el:Element){
142
- const computedStyle = getComputedStyle(el);
197
+ private getElementSizeProps(element: Element) {
198
+ const sizeProps: SizeProps = {
199
+ size: (element.getBoundingClientRect() as any)[this._propSize],
200
+ margin: calcMarginSize(element, this._scrollDirection),
201
+ };
202
+ return sizeProps;
203
+ }
143
204
 
144
- if(OverflowDirection.HORIZONTAL === this._scrollDirection){
145
- return (parseInt(computedStyle.marginLeft || '0') + parseInt(computedStyle.marginRight || '0'));
205
+ private calcChildrenSize(children:Element[]):number{
206
+ let sumChildren = 0;
207
+ sumChildren += this._deltaSize;
208
+ Array.from(children).forEach(el => sumChildren += this.calcElementSize(el));
209
+ return sumChildren;
210
+ }
211
+
212
+ private calcElementSize(el: Element) {
213
+ let size = 0
214
+ if (this.isOverFlowed(el)) {
215
+ const sizeProps = this._hiddenItemsProps.get(el);
216
+ size += sizeProps?.size ?? 0;
217
+ size += sizeProps?.margin ?? 0;
218
+ return size;
146
219
  }
147
220
 
148
- return (parseInt(computedStyle.marginTop || '0') + parseInt(computedStyle.marginBottom || '0'));
221
+ size += (el.getBoundingClientRect() as any)[this._propSize];
222
+ size += calcMarginSize(el, this._scrollDirection);
223
+ return size;
149
224
  }
150
225
 
226
+ private isOverFlowed(el: Element) {
227
+ return el.classList.contains(OVERFLOWED_CLASS_NAME);
228
+ }
229
+ }
230
+
231
+ export interface OverFlowWatcherParams {
232
+ element:HTMLElement,
233
+ callback:OnOverflowCallBack,
234
+ overFlowDirection?:OverflowDirection,
235
+ deltaSize?:number,
236
+ debounce?: number,
237
+ notOverFlow?: string[]
238
+ }
239
+
240
+ interface SizeProps {
241
+ size: number,
242
+ margin: number,
151
243
  }
@@ -0,0 +1,34 @@
1
+ import { calcMarginSize } from '../../src/utils/ElementUtils';
2
+ import { OverflowDirection } from '../../src';
3
+
4
+ describe('calcMarginSize', () => {
5
+ it('should calculate correctly the size of horizontal margin', () => {
6
+ const element = document.createElement('div');
7
+ element.style.marginLeft = '10px';
8
+ element.style.marginRight = '15px';
9
+ document.body.appendChild(element);
10
+
11
+ const size = calcMarginSize(element, OverflowDirection.HORIZONTAL);
12
+ expect(size).toBe(25);
13
+ });
14
+
15
+ it('should calculate correctly the size of vertical margin', () => {
16
+ const element = document.createElement('div');
17
+ element.style.marginTop = '5px';
18
+ element.style.marginBottom = '20px';
19
+ document.body.appendChild(element);
20
+
21
+ const size = calcMarginSize(element, OverflowDirection.VERTICAL);
22
+ expect(size).toBe(25);
23
+ });
24
+
25
+ it('should threat values defined as zero', () => {
26
+ const element = document.createElement('div');
27
+ document.body.appendChild(element);
28
+
29
+ const horizontalSize = calcMarginSize(element, OverflowDirection.HORIZONTAL);
30
+ const verticalSize = calcMarginSize(element, OverflowDirection.VERTICAL);
31
+ expect(horizontalSize).toBe(0);
32
+ expect(verticalSize).toBe(0);
33
+ });
34
+ });
@@ -1,118 +1,152 @@
1
- import OverFlowWatcher, { OnOverflowCallBack, OverflowDirection } from '../../src/utils/OverflowWatcher';
2
-
3
- describe('OverflowWatcher', function () {
4
-
5
- let element: HTMLElement;
6
- let callbackHorizontalDirection: OnOverflowCallBack;
7
- let watcherHorizontalDirection: OverFlowWatcher;
8
- let callbackVerticalDirection: OnOverflowCallBack;
9
- let watcherVerticallDirection: OverFlowWatcher;
10
- let child1:HTMLDivElement;
11
- let child2:HTMLDivElement;
12
-
13
- beforeEach(() => {
14
-
15
- global.ResizeObserver = jest.fn().mockImplementation(() => ({
16
- observe: jest.fn(),
17
- unobserve: jest.fn(),
18
- disconnect: jest.fn(),
19
- }))
20
-
21
- element = document.createElement('div');
22
-
23
- //Horizontal
24
- callbackHorizontalDirection = jest.fn();
25
- watcherHorizontalDirection = new OverFlowWatcher(element, callbackHorizontalDirection);
26
-
27
-
28
- //Vertical
29
- callbackVerticalDirection = jest.fn();
30
- watcherVerticallDirection = new OverFlowWatcher(element, callbackVerticalDirection, OverflowDirection.VERTICAL);
31
-
32
- child1 = document.createElement('div');
33
- child2 = document.createElement('div');
34
-
35
- child1.style.width = '50px';
36
- child2.style.width = '100px';
37
- child1.style.height = '50px';
38
- child2.style.height = '100px';
39
-
40
- //TODO: No futuro utilizar JSDOM para não precisar fazer mock dessas coisas
41
- child1['getBoundingClientRect'] = () => {
42
- return {
43
- width: 50,
44
- height: 50,
45
- } as DOMRect;
46
- }
47
-
48
- child2['getBoundingClientRect'] = () => {
49
- return {
50
- width: 100,
51
- height: 100,
52
- } as DOMRect;
53
- }
54
-
55
- element.appendChild(child1);
56
- element.appendChild(child2);
57
- });
58
-
59
- afterEach(() => {
60
- watcherHorizontalDirection.destroy();
61
- watcherVerticallDirection.destroy();
62
- });
63
-
64
- test('constructor initializes properties correctly', () => {
65
- expect(watcherHorizontalDirection['_onResize']).toBe(callbackHorizontalDirection);
66
- expect(watcherHorizontalDirection['_scrollDirection']).toBe(OverflowDirection.HORIZONTAL);
67
- expect(watcherHorizontalDirection['_deltaSize']).toBe(10);
68
- });
69
-
70
- test('handleResize horizontal calls callback with hidden items', () => {
71
- const entries: any[] = [{
72
- contentRect: {
73
- width: 120
74
- },
75
- target: element
76
- }];
77
-
78
- watcherHorizontalDirection['handleResize'](entries);
79
-
80
- expect(callbackHorizontalDirection).toHaveBeenCalledWith([child2]);
81
-
82
- entries[0].contentRect.width = 100;
83
- watcherHorizontalDirection['handleResize'](entries);
84
-
85
- expect(callbackHorizontalDirection).toHaveBeenCalledWith([child2]);
86
- });
87
-
88
- test('handleResize vertical calls callback with hidden items', () => {
89
- const entries: any[] = [{
90
- contentRect: {
91
- height: 120
92
- },
93
- target: element
94
- }];
95
-
96
- watcherVerticallDirection['handleResize'](entries);
97
-
98
- expect(callbackVerticalDirection).toHaveBeenCalledWith([child2]);
99
- });
100
-
101
- test('isChangedSize returns true if no previous size', () => {
102
- const newContentRect: DOMRectReadOnly = { width: 100 } as DOMRectReadOnly;
103
- watcherHorizontalDirection['_lastContentRect'] = undefined;
104
-
105
- const result = watcherHorizontalDirection['isChangedSize'](newContentRect);
106
-
107
- expect(result).toBe(true);
108
- });
109
-
110
- test('prevent resize without entries', () => {
111
- const entries: any[] = [];
112
-
113
- watcherHorizontalDirection['handleResize'](entries);
114
-
115
- expect(callbackHorizontalDirection).toHaveBeenCalledTimes(0);
116
- });
117
-
118
- });
1
+ import OverflowWatcher, { OverFlowWatcherParams, OverflowDirection, OVERFLOWED_CLASS_NAME } from '../../src/utils/OverflowWatcher';
2
+
3
+ describe('OverflowWatcher', () => {
4
+ let mockElement: HTMLElement;
5
+ let mockCallback: jest.Mock;
6
+ let watcherInstance: OverflowWatcher;
7
+
8
+ beforeEach(() => {
9
+ mockElement = document.createElement('div');
10
+ Object.defineProperty(mockElement, 'clientWidth', { value: 90, configurable: true });
11
+ document.body.appendChild(mockElement);
12
+ mockCallback = jest.fn();
13
+
14
+ const params: OverFlowWatcherParams = {
15
+ element: mockElement,
16
+ callback: mockCallback,
17
+ overFlowDirection: OverflowDirection.HORIZONTAL,
18
+ debounce: 1,
19
+ deltaSize: 1,
20
+ notOverFlow: ['testId'],
21
+ };
22
+
23
+ watcherInstance = new OverflowWatcher(params);
24
+ mockElement.getBoundingClientRect = jest.fn().mockReturnValue(getMockedRectClientValue(200));
25
+ setPrivateField(watcherInstance, '_lastContainerSize', 100);
26
+ setPrivateField(watcherInstance, '_lastContainerInstance', mockElement);
27
+ });
28
+
29
+ afterEach(() => {
30
+ watcherInstance.destroy();
31
+ });
32
+
33
+ it('should initialize with provided parameters', () => {
34
+ expect(watcherInstance).toBeDefined();
35
+ });
36
+
37
+ it('should disconect ResizeObserver when destroy is called', () => {
38
+ const disconnectSpy = jest.spyOn(ResizeObserver.prototype, 'disconnect');
39
+ watcherInstance.destroy();
40
+ expect(disconnectSpy).toHaveBeenCalled();
41
+ });
42
+
43
+ it('Should call callback on forceUpdate', () => {
44
+ appendMockedChildren(mockElement);
45
+ watcherInstance.forceUpdate();
46
+ expect(mockCallback).toHaveBeenCalled();
47
+ });
48
+
49
+ it('Should call callback on forceUpdate with childSpan', () => {
50
+ appendMockedChildren(mockElement);
51
+ watcherInstance.forceUpdate();
52
+
53
+ const childSpan = mockElement.children[1];
54
+ expect(mockCallback).toHaveBeenCalledWith(expect.arrayContaining([childSpan]));
55
+ });
56
+
57
+ it('Should call callback on forceUpdate with childSpan when notOverFlow is empty', () => {
58
+ appendMockedChildren(mockElement);
59
+
60
+ const params: OverFlowWatcherParams = {
61
+ element: mockElement,
62
+ callback: mockCallback,
63
+ overFlowDirection: OverflowDirection.HORIZONTAL,
64
+ debounce: 1,
65
+ deltaSize: 1,
66
+ };
67
+
68
+ const withNoOverFlow = new OverflowWatcher(params);
69
+ setPrivateField(withNoOverFlow, '_lastContainerSize', 100);
70
+ setPrivateField(withNoOverFlow, '_lastContainerInstance', mockElement);
71
+
72
+ withNoOverFlow.forceUpdate();
73
+
74
+ const childSpan = mockElement.children[1];
75
+ expect(mockCallback).toHaveBeenCalledWith(expect.arrayContaining([childSpan]));
76
+ });
77
+
78
+ it('Should call callback on forceUpdate with childSpan considering overflowed elements', () => {
79
+ appendMockedChildren(mockElement);
80
+ const childButton = mockElement.children[0];
81
+ const childSpan = mockElement.children[1];
82
+ childSpan.classList.add(OVERFLOWED_CLASS_NAME);
83
+
84
+ const hiddemItemsMock: Map<Element, {size: number, margin: number}> = new Map();
85
+ hiddemItemsMock.set(childSpan, {size: 50, margin: 0});
86
+ hiddemItemsMock.set(childButton, {size: 0, margin: 50});
87
+ setPrivateField(watcherInstance, '_hiddenItemsProps', hiddemItemsMock);
88
+
89
+ watcherInstance.forceUpdate();
90
+
91
+ expect(mockCallback).toHaveBeenCalledWith(expect.arrayContaining([childSpan]));
92
+ });
93
+
94
+ it('Should call callback on forceUpdate with empty list', () => {
95
+ appendMockedChildren(mockElement);
96
+ setPrivateField(watcherInstance, '_lastContainerSize', 200);
97
+ watcherInstance.forceUpdate();
98
+ const childButton = mockElement.children[0];
99
+ const childSpan = mockElement.children[1];
100
+
101
+ expect(mockCallback).not.toHaveBeenCalledWith(expect.arrayContaining([childButton, childSpan]));
102
+ });
103
+
104
+ it('should ignore elements that can not overflow', () => {
105
+ appendMockedChildren(mockElement);
106
+
107
+ const notOverflowElement = document.createElement('div');
108
+ notOverflowElement.setAttribute('data-element-id', 'testId') ;
109
+ notOverflowElement.getBoundingClientRect = jest.fn().mockReturnValue(getMockedRectClientValue(50));
110
+
111
+ const notOverflowElement2 = document.createElement('div');
112
+ notOverflowElement2.setAttribute('data-element-id', 'testId2') ;
113
+ notOverflowElement2.getBoundingClientRect = jest.fn().mockReturnValue(getMockedRectClientValue(50));
114
+
115
+ mockElement.appendChild(notOverflowElement);
116
+ mockElement.appendChild(notOverflowElement2);
117
+
118
+ watcherInstance.addNotOverFlowElement('testId');
119
+ watcherInstance.addNotOverFlowElement('testId2');
120
+
121
+ watcherInstance.forceUpdate();
122
+
123
+ expect(mockCallback).not.toHaveBeenCalledWith(expect.arrayContaining([notOverflowElement]));
124
+ });
125
+
126
+ it('Should not call callback on forceUpdate', () => {
127
+ watcherInstance.forceUpdate();
128
+ expect(mockCallback).not.toHaveBeenCalled();
129
+ });
130
+
131
+ });
132
+
133
+ function appendMockedChildren(mockElement: HTMLElement) {
134
+ const chilldButton = document.createElement('button');
135
+ const childSpan = document.createElement('span');
136
+
137
+ chilldButton.getBoundingClientRect = jest.fn().mockReturnValue(getMockedRectClientValue(50));
138
+ childSpan.getBoundingClientRect = jest.fn().mockReturnValue(getMockedRectClientValue(50));
139
+
140
+ mockElement.appendChild(chilldButton);
141
+ mockElement.appendChild(childSpan);
142
+ }
143
+
144
+ function getMockedRectClientValue(size: number) {
145
+ return {
146
+ x: 0, y: 0, width: size, height: 10, top: 0, right: 0, bottom: 0, left: 0, toJSON: () => {}
147
+ };
148
+ }
149
+
150
+ function setPrivateField<T>(instance: T, fieldName: string, value: any): void {
151
+ (instance as any)[fieldName] = value;
152
+ }