@percy/dom 1.31.1 → 1.31.2-beta.1

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.
package/README.md CHANGED
@@ -65,7 +65,7 @@ to a new stylesheet inserted into the document's head.
65
65
  Canvas elements' drawing buffers are serialized as data URIs and the canvas elements are replaced
66
66
  with image elements. The image elements reference the serialized data URI and have the same HTML
67
67
  attributes as their respective canvas elements. The image elements also have a max-width of 100% to
68
- accomidate responsive layouts in situations where canvases may be expected to resize with JS.
68
+ accommodate responsive layouts in situations where canvases may be expected to resize with JS.
69
69
 
70
70
  ### Video elements
71
71
 
package/dist/bundle.js CHANGED
@@ -336,14 +336,50 @@
336
336
  }
337
337
  }
338
338
 
339
+ // Helper function to create and insert image element
340
+ function createAndInsertImageElement(canvas, clone, percyElementId, imageUrl) {
341
+ let img = document.createElement('img');
342
+
343
+ // copy canvas element attributes to the image element
344
+ for (let {
345
+ name,
346
+ value
347
+ } of canvas.attributes) {
348
+ img.setAttribute(name, value);
349
+ }
350
+
351
+ // mark the image as serialized and set src attribute
352
+ img.setAttribute('data-percy-canvas-serialized', '');
353
+ img.setAttribute('data-percy-serialized-attribute-src', imageUrl);
354
+
355
+ // set a default max width to account for canvases that might resize with JS
356
+ img.style.maxWidth = img.style.maxWidth || '100%';
357
+
358
+ // insert the image into the cloned DOM and remove the cloned canvas element
359
+ let cloneEl = clone.querySelector(`[data-percy-element-id=${percyElementId}]`);
360
+ if (!cloneEl) {
361
+ throw new Error(`Clone element not found for percy-element-id: ${percyElementId}`);
362
+ }
363
+
364
+ // `parentElement` for elements directly under shadow root is `null` -> Incase of Nested Shadow DOM.
365
+ if (cloneEl.parentElement) {
366
+ cloneEl.parentElement.insertBefore(img, cloneEl);
367
+ } else {
368
+ clone.insertBefore(img, cloneEl);
369
+ }
370
+ cloneEl.remove();
371
+ }
372
+
339
373
  // Serialize in-memory canvas elements into images.
340
374
  function serializeCanvas(ctx) {
341
375
  let {
342
376
  dom,
343
377
  clone,
344
- resources
378
+ resources,
379
+ ignoreCanvasSerializationErrors
345
380
  } = ctx;
346
381
  for (let canvas of dom.querySelectorAll('canvas')) {
382
+ let percyElementId = canvas.getAttribute('data-percy-element-id');
347
383
  try {
348
384
  // Note: the `.toDataURL` API requires WebGL canvas elements to use
349
385
  // `preserveDrawingBuffer: true`. This is because `.toDataURL` uses the
@@ -353,41 +389,25 @@
353
389
  // skip empty canvases
354
390
  if (!dataUrl || dataUrl === 'data:,') continue;
355
391
 
356
- // get the element's percy id and create a resource for it
357
- let percyElementId = canvas.getAttribute('data-percy-element-id');
392
+ // create a resource for the canvas data
358
393
  let resource = resourceFromDataURL(percyElementId, dataUrl);
359
394
  resources.add(resource);
360
395
 
361
- // create an image element in the cloned dom
362
- let img = document.createElement('img');
363
- // use a data attribute to avoid making a real request
364
- img.setAttribute('data-percy-serialized-attribute-src', resource.url);
365
-
366
- // copy canvas element attributes to the image element such as style, class,
367
- // or data attributes that may be targeted by CSS
368
- for (let {
369
- name,
370
- value
371
- } of canvas.attributes) {
372
- img.setAttribute(name, value);
373
- }
374
-
375
- // mark the image as serialized (can be targeted by CSS)
376
- img.setAttribute('data-percy-canvas-serialized', '');
377
- // set a default max width to account for canvases that might resize with JS
378
- img.style.maxWidth = img.style.maxWidth || '100%';
379
-
380
- // insert the image into the cloned DOM and remove the cloned canvas element
381
- let cloneEl = clone.querySelector(`[data-percy-element-id=${percyElementId}]`);
382
- // `parentElement` for elements directly under shadow root is `null` -> Incase of Nested Shadow DOM.
383
- if (cloneEl.parentElement) {
384
- cloneEl.parentElement.insertBefore(img, cloneEl);
396
+ // create and insert image element with the resource URL
397
+ createAndInsertImageElement(canvas, clone, percyElementId, resource.url);
398
+ } catch (err) {
399
+ if (ignoreCanvasSerializationErrors) {
400
+ try {
401
+ // create and insert image element with empty src
402
+ ctx.warnings.add('Canvas Serialization failed, Replaced canvas with empty Image');
403
+ ctx.warnings.add('Error: ' + err.message);
404
+ createAndInsertImageElement(canvas, clone, percyElementId, '');
405
+ } catch (fallbackErr) {
406
+ ctx.warnings.add('Error creating fallback image element: ' + fallbackErr.message);
407
+ }
385
408
  } else {
386
- clone.insertBefore(img, cloneEl);
409
+ handleErrors(err, 'Error serializing canvas element: ', canvas);
387
410
  }
388
- cloneEl.remove();
389
- } catch (err) {
390
- handleErrors(err, 'Error serializing canvas element: ', canvas);
391
411
  }
392
412
  }
393
413
  }
@@ -439,10 +459,24 @@
439
459
  if (!['img', 'iframe'].includes((_domElement$tagName = domElement.tagName) === null || _domElement$tagName === void 0 ? void 0 : _domElement$tagName.toLowerCase())) return;
440
460
  domElement.removeAttribute('loading');
441
461
  }
462
+ function serializeScrollState(original, clone) {
463
+ if (!original || !clone) return;
464
+
465
+ // Check and set scrollTop if it exists and has a value
466
+ if (typeof original.scrollTop === 'number' && original.scrollTop !== 0) {
467
+ clone.setAttribute('data-percy-scrolltop', original.scrollTop.toString());
468
+ }
469
+
470
+ // Check and set scrollLeft if it exists and has a value
471
+ if (typeof original.scrollLeft === 'number' && original.scrollLeft !== 0) {
472
+ clone.setAttribute('data-percy-scrollleft', original.scrollLeft.toString());
473
+ }
474
+ }
442
475
 
443
476
  // All transformations that we need to apply for a successful discovery and stable render
444
- function applyElementTransformations(domElement) {
477
+ function applyElementTransformations(originalElement, domElement) {
445
478
  dropLoadingAttribute(domElement);
479
+ serializeScrollState(originalElement, domElement);
446
480
  }
447
481
 
448
482
  let mimetype = null;
@@ -572,7 +606,7 @@
572
606
  }
573
607
 
574
608
  // We apply any element transformations here to avoid another treeWalk
575
- applyElementTransformations(clone);
609
+ applyElementTransformations(node, clone);
576
610
  serializeBase64(clone, resources, cache);
577
611
  parent.appendChild(clone);
578
612
 
@@ -729,7 +763,8 @@
729
763
  domTransformation = options === null || options === void 0 ? void 0 : options.dom_transformation,
730
764
  stringifyResponse = options === null || options === void 0 ? void 0 : options.stringify_response,
731
765
  disableShadowDOM = options === null || options === void 0 ? void 0 : options.disable_shadow_dom,
732
- reshuffleInvalidTags = options === null || options === void 0 ? void 0 : options.reshuffle_invalid_tags
766
+ reshuffleInvalidTags = options === null || options === void 0 ? void 0 : options.reshuffle_invalid_tags,
767
+ ignoreCanvasSerializationErrors = options === null || options === void 0 ? void 0 : options.ignore_canvas_serialization_errors
733
768
  } = options || {};
734
769
 
735
770
  // keep certain records throughout serialization
@@ -740,7 +775,8 @@
740
775
  cache: new Map(),
741
776
  shadowRootElements: [],
742
777
  enableJavaScript,
743
- disableShadowDOM
778
+ disableShadowDOM,
779
+ ignoreCanvasSerializationErrors
744
780
  };
745
781
  ctx.dom = dom;
746
782
  ctx.clone = cloneNodeAndShadow(ctx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@percy/dom",
3
- "version": "1.31.1",
3
+ "version": "1.31.2-beta.1",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "publishConfig": {
11
11
  "access": "public",
12
- "tag": "latest"
12
+ "tag": "beta"
13
13
  },
14
14
  "main": "dist/bundle.js",
15
15
  "browser": "dist/bundle.js",
@@ -35,5 +35,5 @@
35
35
  "devDependencies": {
36
36
  "interactor.js": "^2.0.0-beta.10"
37
37
  },
38
- "gitHead": "56d0fe4722eea63cfe8a6ea7aa02cf00d465a85d"
38
+ "gitHead": "c8ba03fdd3ab7543f741a68eb5c357e38b48129e"
39
39
  }