Repository: sweetalert2/sweetalert2 (js)

Issue:
Add possibility to disable opacity animation for easier snapshot testing
### New feature motivation

I think it will be helpful for developers to have a possibility to disable popup's appearing animation with an option. As an example it could be `disableAnimations` option.

For now I have a special helper in tests to check is modal already fully visible on a screen or not. 

I need this to avoid flacky tests while snapshot testing & I think it will be helpful for everyone, cause right now this animation is hardcoded.

### New feature description

--

### New feature implementation

Add a condition for the rows where the animation is added: 
https://github.com/sweetalert2/sweetalert2/blob/main/src/utils/openPopup.js#L97

Pull request description:
feat: add animation: boolean param
closes #2677

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Introduced an animation toggle for SweetAlert popups, allowing users to enable or disable animations.

- **Tests**
  - Added new test cases to verify the presence or absence of animations in SweetAlert modals.

- **Refactor**
  - Streamlined the handling of animations within the SweetAlert utility functions.

- **Documentation**
  - Updated type definitions to include the new `animation` property for SweetAlert options.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Patch:
diff --git a/cypress/utils.js b/cypress/utils.js
index 4d1379eb..a8cb7a92 100644
--- a/cypress/utils.js
+++ b/cypress/utils.js
@@ -9,19 +9,7 @@ export const isHidden = (elem) => !isVisible(elem)
 
 export const TIMEOUT = 10
 
-// We *only* access `Swal` through this module, so that we can be sure `initialSwalPropNames` is set properly
-export const SwalWithoutAnimation = Swal.mixin({
-  showClass: {
-    container: '',
-    popup: '',
-    icon: '',
-  },
-  hideClass: {
-    container: '',
-    popup: '',
-    icon: '',
-  },
-})
+export const SwalWithoutAnimation = Swal.mixin({ animation: false })
 
 export const dispatchCustomEvent = (elem, eventName, eventDetail = {}) => {
   const event = new CustomEvent(eventName, {
diff --git a/src/SweetAlert.js b/src/SweetAlert.js
index 53365ab0..24d54ba6 100644
--- a/src/SweetAlert.js
+++ b/src/SweetAlert.js
@@ -158,6 +158,12 @@ const prepareParams = (userParams, mixinParams) => {
   const params = Object.assign({}, defaultParams, mixinParams, templateParams, userParams) // precedence is described in #2131
   params.showClass = Object.assign({}, defaultParams.showClass, params.showClass)
   params.hideClass = Object.assign({}, defaultParams.hideClass, params.hideClass)
+  if (params.animation === false) {
+    params.showClass = {
+      backdrop: 'swal2-noanimation',
+    }
+    params.hideClass = {}
+  }
   return params
 }
 
diff --git a/src/utils/openPopup.js b/src/utils/openPopup.js
index 8e72b8b2..95fd1daf 100644
--- a/src/utils/openPopup.js
+++ b/src/utils/openPopup.js
@@ -96,15 +96,19 @@ const fixScrollContainer = (container, scrollbarPadding, initialBodyOverflow) =>
  */
 const addClasses = (container, popup, params) => {
   dom.addClass(container, params.showClass.backdrop)
-  // this workaround with opacity is needed for https://github.com/sweetalert2/sweetalert2/issues/2059
-  popup.style.setProperty('opacity', '0', 'important')
-  dom.show(popup, 'grid')
-  setTimeout(() => {
-    // Animate popup right after showing it
-    dom.addClass(popup, params.showClass.popup)
-    // and remove the opacity workaround
-    popup.style.removeProperty('opacity')
-  }, SHOW_CLASS_TIMEOUT) // 10ms in order to fix #2062
+  if (params.animation) {
+    // this workaround with opacity is needed for https://github.com/sweetalert2/sweetalert2/issues/2059
+    popup.style.setProperty('opacity', '0', 'important')
+    dom.show(popup, 'grid')
+    setTimeout(() => {
+      // Animate popup right after showing it
+      dom.addClass(popup, params.showClass.popup)
+      // and remove the opacity workaround
+      popup.style.removeProperty('opacity')
+    }, SHOW_CLASS_TIMEOUT) // 10ms in order to fix #2062
+  } else {
+    dom.show(popup, 'grid')
+  }
 
   dom.addClass([document.documentElement, document.body], swalClasses.shown)
   if (params.heightAuto && params.backdrop && !params.toast) {
diff --git a/src/utils/params.js b/src/utils/params.js
index 0348ab1e..3dbbede6 100644
--- a/src/utils/params.js
+++ b/src/utils/params.js
@@ -11,6 +11,7 @@ export const defaultParams = {
   iconHtml: undefined,
   template: undefined,
   toast: false,
+  animation: true,
   showClass: {
     popup: 'swal2-show',
     backdrop: 'swal2-backdrop-show',
diff --git a/sweetalert2.d.ts b/sweetalert2.d.ts
index b750fc93..f9299b1c 100644
--- a/sweetalert2.d.ts
+++ b/sweetalert2.d.ts
@@ -691,6 +691,13 @@ declare module 'sweetalert2' {
      */
     grow?: SweetAlertGrow
 
+    /**
+     * If set to `false`, the popup animation will be disabled.
+     *
+     * @default true
+     */
+    animation?: boolean
+
     /**
      * CSS classes for animations when showing a popup (fade in)
      *


Test patch:
diff --git a/cypress/e2e/api.cy.js b/cypress/e2e/api.cy.js
index 0633d0ae..43219eec 100644
--- a/cypress/e2e/api.cy.js
+++ b/cypress/e2e/api.cy.js
@@ -6,9 +6,6 @@ describe('API', () => {
   it('properties of `Swal` class are consistent', (done) => {
     const assertConsistent = (postfix) => {
       const currentSwalPropNames = Object.keys(Swal)
-      // const extraPropNames = currentSwalPropNames.filter(key => !initialSwalPropNames.includes(key))
-      // expect(extraPropNames.length, 0).to.be.eql(`# of extra properties ${postfix}`)
-      // expect(extraPropNames.join(','), '').to.be.eql(`extra property names ${postfix}`)
       const missingProps = currentSwalPropNames.filter((key) => !currentSwalPropNames.includes(key))
       expect(missingProps.length).to.equal(0, `# of missing properties ${postfix}`)
       expect(missingProps.join(',')).to.equal('', `missing property names ${postfix}`)
diff --git a/cypress/e2e/tests.cy.js b/cypress/e2e/tests.cy.js
index 05176334..d80de1b6 100644
--- a/cypress/e2e/tests.cy.js
+++ b/cypress/e2e/tests.cy.js
@@ -486,6 +486,32 @@ describe('Miscellaneous tests', function () {
     })
   })
 
+  it('animation enabled', (done) => {
+    Swal.fire({
+      animation: true,
+      didOpen: () => {
+        setTimeout(() => {
+          expect(Array.from(Swal.getPopup().classList)).to.contain('swal2-show')
+          expect(Array.from(Swal.getContainer().classList)).not.to.contain('swal2-noanimation')
+          done()
+        }, SHOW_CLASS_TIMEOUT)
+      },
+    })
+  })
+
+  it('animation disabled', (done) => {
+    Swal.fire({
+      animation: false,
+      didOpen: () => {
+        setTimeout(() => {
+          expect(Array.from(Swal.getPopup().classList)).not.to.contain('swal2-show')
+          expect(Array.from(Swal.getContainer().classList)).to.contain('swal2-noanimation')
+          done()
+        }, SHOW_CLASS_TIMEOUT)
+      },
+    })
+  })
+
   it('params validation', () => {
     expect(Swal.isValidParameter('title')).to.be.true
     expect(Swal.isValidParameter('foobar')).to.be.false