kamrify commited on
Commit
fcd0eab
·
1 Parent(s): f23af63

Fix - Breaks the UI when element has fixed or absolute position

Browse files
.eslintrc.json CHANGED
@@ -11,6 +11,7 @@
11
  "func-names": "off",
12
  "no-bitwise": "off",
13
  "class-methods-use-this": "off",
 
14
  "no-param-reassign": [
15
  "off"
16
  ],
 
11
  "func-names": "off",
12
  "no-bitwise": "off",
13
  "class-methods-use-this": "off",
14
+ "prefer-destructuring": "off",
15
  "no-param-reassign": [
16
  "off"
17
  ],
src/common/constants.js CHANGED
@@ -13,6 +13,7 @@ export const ID_STAGE = 'driver-highlighted-element-stage';
13
  export const ID_POPOVER = 'driver-popover-item';
14
 
15
  export const CLASS_DRIVER_HIGHLIGHTED_ELEMENT = 'driver-highlighted-element';
 
16
 
17
  export const CLASS_NO_ANIMATION = 'driver-no-animation';
18
  export const CLASS_POPOVER_TIP = 'driver-popover-tip';
 
13
  export const ID_POPOVER = 'driver-popover-item';
14
 
15
  export const CLASS_DRIVER_HIGHLIGHTED_ELEMENT = 'driver-highlighted-element';
16
+ export const CLASS_POSITION_RELATIVE = 'driver-position-relative';
17
 
18
  export const CLASS_NO_ANIMATION = 'driver-no-animation';
19
  export const CLASS_POPOVER_TIP = 'driver-popover-tip';
src/common/utils.js CHANGED
@@ -11,3 +11,24 @@ export const createNodeFromString = (htmlString) => {
11
  // Change this to div.childNodes to support multiple top-level nodes
12
  return div.firstChild;
13
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  // Change this to div.childNodes to support multiple top-level nodes
12
  return div.firstChild;
13
  };
14
+
15
+ /**
16
+ * Gets the CSS property from the given element
17
+ * @param {HTMLElement|Node} element
18
+ * @param {string} propertyName
19
+ * @return {string}
20
+ */
21
+ export const getStyleProperty = (element, propertyName) => {
22
+ let propertyValue = '';
23
+
24
+ if (element.currentStyle) {
25
+ propertyValue = element.currentStyle[propertyName];
26
+ } else if (document.defaultView && document.defaultView.getComputedStyle) {
27
+ propertyValue = document.defaultView
28
+ .getComputedStyle(element, null)
29
+ .getPropertyValue(propertyName);
30
+ }
31
+
32
+ return propertyValue && propertyValue.toLowerCase ? propertyValue.toLowerCase() : propertyValue;
33
+ };
34
+
src/core/element.js CHANGED
@@ -1,5 +1,6 @@
 
 
1
  import Position from './position';
2
- import { ANIMATION_DURATION_MS, CLASS_DRIVER_HIGHLIGHTED_ELEMENT } from '../common/constants';
3
 
4
  /**
5
  * Wrapper around DOMElements to enrich them
@@ -36,27 +37,6 @@ export default class Element {
36
  this.animationTimeout = null;
37
  }
38
 
39
- /**
40
- * Gets the screen co-ordinates (x,y) for the current dom element
41
- * @returns {{x: number, y: number}}
42
- * @private
43
- */
44
- getScreenCoordinates() {
45
- let tempNode = this.node;
46
-
47
- let x = this.document.documentElement.offsetLeft;
48
- let y = this.document.documentElement.offsetTop;
49
-
50
- if (tempNode.offsetParent) {
51
- do {
52
- x += tempNode.offsetLeft;
53
- y += tempNode.offsetTop;
54
- } while (tempNode = tempNode.offsetParent);
55
- }
56
-
57
- return { x, y };
58
- }
59
-
60
  /**
61
  * Checks if the current element is visible in viewport
62
  * @returns {boolean}
@@ -128,24 +108,20 @@ export default class Element {
128
  * @public
129
  */
130
  getCalculatedPosition() {
131
- const coordinates = this.getScreenCoordinates();
132
- const position = new Position({
133
- left: Number.MAX_VALUE,
134
- top: Number.MAX_VALUE,
135
- right: 0,
136
- bottom: 0,
137
- });
138
 
139
- // If we have the position for this element
140
- // and the element is visible on screen (has some height)
141
- if (typeof coordinates.x === 'number' && typeof coordinates.y === 'number' && (this.node.offsetWidth > 0 || this.node.offsetHeight > 0)) {
142
- position.left = Math.min(position.left, coordinates.x);
143
- position.top = Math.min(position.top, coordinates.y);
144
- position.right = Math.max(position.right, coordinates.x + this.node.offsetWidth);
145
- position.bottom = Math.max(position.bottom, coordinates.y + this.node.offsetHeight);
146
- }
147
 
148
- return position;
 
 
 
 
 
149
  }
150
 
151
  /**
@@ -160,7 +136,7 @@ export default class Element {
160
  this.hideStage();
161
  }
162
 
163
- this.node.classList.remove(CLASS_DRIVER_HIGHLIGHTED_ELEMENT);
164
 
165
  // If there was any animation in progress, cancel that
166
  this.window.clearTimeout(this.animationTimeout);
@@ -170,6 +146,10 @@ export default class Element {
170
  }
171
  }
172
 
 
 
 
 
173
  /**
174
  * Checks if the given element is same as the current element
175
  * @param {Element} element
@@ -202,7 +182,7 @@ export default class Element {
202
  this.showPopover();
203
  this.showStage();
204
 
205
- this.node.classList.add(CLASS_DRIVER_HIGHLIGHTED_ELEMENT);
206
 
207
  const highlightedElement = this;
208
  const popoverElement = this.popover;
@@ -220,6 +200,33 @@ export default class Element {
220
  }
221
  }
222
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  /**
224
  * Shows the stage behind the element
225
  * @public
 
1
+ import { ANIMATION_DURATION_MS, CLASS_DRIVER_HIGHLIGHTED_ELEMENT, CLASS_POSITION_RELATIVE } from '../common/constants';
2
+ import { getStyleProperty } from '../common/utils';
3
  import Position from './position';
 
4
 
5
  /**
6
  * Wrapper around DOMElements to enrich them
 
37
  this.animationTimeout = null;
38
  }
39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  /**
41
  * Checks if the current element is visible in viewport
42
  * @returns {boolean}
 
108
  * @public
109
  */
110
  getCalculatedPosition() {
111
+ const body = this.document.body;
112
+ const documentElement = this.document.documentElement;
113
+ const window = this.window;
 
 
 
 
114
 
115
+ const scrollTop = this.window.pageYOffset || documentElement.scrollTop || body.scrollTop;
116
+ const scrollLeft = window.pageXOffset || documentElement.scrollLeft || body.scrollLeft;
117
+ const elementRect = this.node.getBoundingClientRect();
 
 
 
 
 
118
 
119
+ return new Position({
120
+ top: elementRect.top + scrollTop,
121
+ left: elementRect.left + scrollLeft,
122
+ right: elementRect.left + scrollLeft + elementRect.width,
123
+ bottom: elementRect.top + scrollTop + elementRect.height,
124
+ });
125
  }
126
 
127
  /**
 
136
  this.hideStage();
137
  }
138
 
139
+ this.removeHighlightClasses();
140
 
141
  // If there was any animation in progress, cancel that
142
  this.window.clearTimeout(this.animationTimeout);
 
146
  }
147
  }
148
 
149
+ removeHighlightClasses() {
150
+ this.node.classList.remove(CLASS_DRIVER_HIGHLIGHTED_ELEMENT);
151
+ }
152
+
153
  /**
154
  * Checks if the given element is same as the current element
155
  * @param {Element} element
 
182
  this.showPopover();
183
  this.showStage();
184
 
185
+ this.addHighlightClasses();
186
 
187
  const highlightedElement = this;
188
  const popoverElement = this.popover;
 
200
  }
201
  }
202
 
203
+ addHighlightClasses() {
204
+ this.node.classList.add(CLASS_DRIVER_HIGHLIGHTED_ELEMENT);
205
+
206
+ if (this.canMakeRelative()) {
207
+ this.node.classList.add(CLASS_POSITION_RELATIVE);
208
+ }
209
+ }
210
+
211
+ canMakeRelative() {
212
+ const currentPosition = this.getStyleProperty('position');
213
+ const avoidPositionsList = ['absolute', 'fixed', 'relative'];
214
+
215
+ // Because if the element has any of these positions, making it
216
+ // relative will break the UI
217
+ return !avoidPositionsList.includes(currentPosition);
218
+ }
219
+
220
+ /**
221
+ * Get an element CSS property on the page
222
+ * @param {string} property
223
+ * @returns string
224
+ * @private
225
+ */
226
+ getStyleProperty(property) {
227
+ return getStyleProperty(this.node, property);
228
+ }
229
+
230
  /**
231
  * Shows the stage behind the element
232
  * @public
src/driver.scss CHANGED
@@ -172,5 +172,8 @@ div#driver-highlighted-element-stage {
172
 
173
  .driver-highlighted-element {
174
  z-index: $highlighted-element-zindex !important;
 
 
 
175
  position: relative;
176
  }
 
172
 
173
  .driver-highlighted-element {
174
  z-index: $highlighted-element-zindex !important;
175
+ }
176
+
177
+ .driver-position-relative {
178
  position: relative;
179
  }
types/index.d.ts CHANGED
@@ -199,12 +199,6 @@ declare module 'driver.js' {
199
  window: Window,
200
  document: Document);
201
 
202
- /**
203
- * Gets the screen coordinates for the current DOM Element
204
- * @return {Driver.ScreenCoordinates}
205
- */
206
- public getScreenCoordinates(): Driver.ScreenCoordinates;
207
-
208
  /**
209
  * Checks if the give element is in view port or not
210
  * @return {boolean}
@@ -280,6 +274,16 @@ declare module 'driver.js' {
280
  * @return {Driver.ElementSize}
281
  */
282
  public getSize(): Driver.ElementSize;
 
 
 
 
 
 
 
 
 
 
283
  }
284
 
285
  class Overlay {
 
199
  window: Window,
200
  document: Document);
201
 
 
 
 
 
 
 
202
  /**
203
  * Checks if the give element is in view port or not
204
  * @return {boolean}
 
274
  * @return {Driver.ElementSize}
275
  */
276
  public getSize(): Driver.ElementSize;
277
+
278
+ /**
279
+ * Removes the highlight classes from current element if any
280
+ */
281
+ private removeHighlightClasses(): void;
282
+
283
+ /**
284
+ * Adds the highlight classes to current element if required
285
+ */
286
+ private addHighlightClasses(): void;
287
  }
288
 
289
  class Overlay {