Repository: exadel-inc/esl
Pull request #2823 (2024-12-13 13:53:17)

ESLPopup: introduce config for easily acessing params


Patch:
diff --git a/src/modules/esl-alert/core/esl-alert.ts b/src/modules/esl-alert/core/esl-alert.ts
index 3412e07de..78bfb4503 100644
--- a/src/modules/esl-alert/core/esl-alert.ts
+++ b/src/modules/esl-alert/core/esl-alert.ts
@@ -18,9 +18,6 @@ export interface ESLAlertActionParams extends ESLToggleableRequestDetails {
   hideTime?: number;
 }
 
-/** @deprecated alias, use {@link ESLAlertActionParams} instead. Will be removed in v5.0.0. */
-export type AlertActionParams = ESLAlertActionParams;
-
 /**
  * ESLAlert component
  * @author Julia Murashko, Alexey Stsefanovich (ala'n)
diff --git a/src/modules/esl-panel/core/esl-panel.ts b/src/modules/esl-panel/core/esl-panel.ts
index 26b5003b0..19da08c40 100644
--- a/src/modules/esl-panel/core/esl-panel.ts
+++ b/src/modules/esl-panel/core/esl-panel.ts
@@ -15,9 +15,6 @@ export interface ESLPanelActionParams extends ESLToggleableActionParams {
   noAnimate?: boolean;
 }
 
-/** @deprecated alias, use {@link ESLPanelActionParams} instead. Will be removed in v5.0.0. */
-export type PanelActionParams = ESLPanelActionParams;
-
 /**
  * ESLPanel component
  * @author Julia Murashko
diff --git a/src/modules/esl-popup/core.ts b/src/modules/esl-popup/core.ts
index e73ead432..d33c55109 100644
--- a/src/modules/esl-popup/core.ts
+++ b/src/modules/esl-popup/core.ts
@@ -2,3 +2,4 @@ export type {ESLPopupTagShape} from './core/esl-popup.shape';
 
 export * from './core/esl-popup';
 export * from './core/esl-popup-placeholder';
+export * from './core/esl-popup-types';
diff --git a/src/modules/esl-popup/core/esl-popup-types.ts b/src/modules/esl-popup/core/esl-popup-types.ts
new file mode 100644
index 000000000..04c31381a
--- /dev/null
+++ b/src/modules/esl-popup/core/esl-popup-types.ts
@@ -0,0 +1,54 @@
+import type {ESLToggleableActionParams} from '../../esl-toggleable/core';
+import type {PositionType, PositionOriginType} from './esl-popup-position';
+
+export interface ESLPopupActionParams extends ESLToggleableActionParams {
+  /** popup position relative to trigger */
+  position?: PositionType;
+  /** clarification of the popup position, whether it should start on the outside of trigger or the inside of trigger */
+  positionOrigin?: PositionOriginType;
+  /** popup behavior if it does not fit in the window */
+  behavior?: string;
+  /** Disable hiding the popup depending on the visibility of the activator */
+  disableActivatorObservation?: boolean;
+  /** Margins on the edges of the arrow. */
+  marginArrow?: number;
+  /** Offset of the arrow as a percentage of the popup edge (0% - at the left edge, 100% - at the right edge, for RTL it is vice versa) */
+  offsetArrow?: string;
+  /** Offset in pixels from the trigger element */
+  offsetTrigger?: number;
+  /**
+   * Offset in pixels from the edges of the container (or window if the container is not defined)
+   *  value as a number for equals x and y offsets
+   *  value as an array for different x and y offsets
+   */
+  offsetContainer?: number | [number, number];
+  /** Margin around the element that is used as the viewport for checking the visibility of the popup activator */
+  intersectionMargin?: string;
+  /** Target to container element to define bounds of popups visibility */
+  container?: string;
+  /** Container element that defines bounds of popups visibility (is not taken into account if the container attr is set on popup) */
+  containerEl?: HTMLElement;
+
+  /** Extra class to add to popup on activation */
+  extraClass?: string;
+  /** Extra styles to add to popup on activation */
+  extraStyle?: string;
+}
+
+export type ProxiedParams = Required<ESLPopupActionParams>;
+
+/** List of ESLPopup config keys */
+export const ESL_POPUP_CONFIG_KEYS: (keyof ESLPopupActionParams)[] = [
+  'position',
+  'positionOrigin',
+  'behavior',
+  'disableActivatorObservation',
+  'marginArrow',
+  'offsetArrow',
+  'offsetTrigger',
+  'offsetContainer',
+  'intersectionMargin',
+  'container',
+  'containerEl',
+  'extraClass',
+  'extraStyle'] as const;
diff --git a/src/modules/esl-popup/core/esl-popup.ts b/src/modules/esl-popup/core/esl-popup.ts
index 6511288dc..30c288a30 100644
--- a/src/modules/esl-popup/core/esl-popup.ts
+++ b/src/modules/esl-popup/core/esl-popup.ts
@@ -6,54 +6,19 @@ import {afterNextRender, rafDecorator} from '../../esl-utils/async/raf';
 import {ESLToggleable} from '../../esl-toggleable/core';
 import {isElement, isRelativeNode, isRTL, Rect, getListScrollParents, getViewportRect} from '../../esl-utils/dom';
 import {parseBoolean, parseNumber, toBooleanAttribute} from '../../esl-utils/misc/format';
-import {copyDefinedKeys} from '../../esl-utils/misc/object';
+import {copy} from '../../esl-utils/misc/object/copy';
 import {ESLIntersectionTarget, ESLIntersectionEvent} from '../../esl-event-listener/core/targets/intersection.target';
 import {calcPopupPosition, isOnHorizontalAxis} from './esl-popup-position';
 import {ESLPopupPlaceholder} from './esl-popup-placeholder';
+import {ESL_POPUP_CONFIG_KEYS} from './esl-popup-types';
 
 import type {ESLToggleableActionParams, ESLA11yType} from '../../esl-toggleable/core';
 import type {PopupPositionConfig, PositionType, PositionOriginType, IntersectionRatioRect} from './esl-popup-position';
+import type {ESLPopupActionParams, ProxiedParams} from './esl-popup-types';
 
 const INTERSECTION_LIMIT_FOR_ADJACENT_AXIS = 0.7;
 const DEFAULT_OFFSET_ARROW = 50;
 
-export interface ESLPopupActionParams extends ESLToggleableActionParams {
-  /** popup position relative to trigger */
-  position?: PositionType;
-  /** clarification of the popup position, whether it should start on the outside of trigger or the inside of trigger */
-  positionOrigin?: PositionOriginType;
-  /** popup behavior if it does not fit in the window */
-  behavior?: string;
-  /** Disable hiding the popup depending on the visibility of the activator */
-  disableActivatorObservation?: boolean;
-  /** Margins on the edges of the arrow. */
-  marginArrow?: number;
-  /** Offset of the arrow as a percentage of the popup edge (0% - at the left edge, 100% - at the right edge, for RTL it is vice versa) */
-  offsetArrow?: string;
-  /** Offset in pixels from the trigger element */
-  offsetTrigger?: number;
-  /**
-   * Offset in pixels from the edges of the container (or window if the container is not defined)
-   *  value as a number for equals x and y offsets
-   *  value as an array for different x and y offsets
-   */
-  offsetContainer?: number | [number, number];
-  /** Margin around the element that is used as the viewport for checking the visibility of the popup activator */
-  intersectionMargin?: string;
-  /** Target to container element to define bounds of popups visibility */
-  container?: string;
-  /** Container element that defines bounds of popups visibility (is not taken into account if the container attr is set on popup) */
-  containerEl?: HTMLElement;
-
-  /** Extra class to add to popup on activation */
-  extraClass?: string;
-  /** Extra styles to add to popup on activation */
-  extraStyle?: string;
-}
-
-/** @deprecated alias, use {@link ESLPopupActionParams} instead, will be removed in v5.0.0 */
-export type PopupActionParams = ESLPopupActionParams;
-
 @ExportNs('Popup')
 export class ESLPopup extends ESLToggleable {
   public static override is = 'esl-popup';
@@ -64,6 +29,9 @@ export class ESLPopup extends ESLToggleable {
     intersectionMargin: '0px'
   };
 
+  /** List of config keys */
+  public static CONFIG_KEYS: string[] = ESL_POPUP_CONFIG_KEYS as string[];
+
   /** Classname of popups arrow element */
   @attr({defaultValue: 'esl-popup-arrow'}) public arrowClass: string;
 
@@ -116,12 +84,30 @@ export class ESLPopup extends ESLToggleable {
 
   public $placeholder: ESLPopupPlaceholder | null;
 
-  protected _extraClass?: string;
-  protected _extraStyle?: string;
+  @memoize()
+  public get config(): ProxiedParams {
+    // eslint-disable-next-line @typescript-eslint/no-this-alias
+    const $popup = this;
+    return new Proxy<ProxiedParams>({} as ProxiedParams, {
+      get<T extends keyof ESLPopupActionParams>(target: ESLPopupActionParams, p: T | symbol): ESLPopupActionParams[T] {
+        return Reflect.get($popup._params, p) ?? Reflect.get($popup, p);
+      },
+      set: (): boolean => false,
+      deleteProperty: (): boolean => false,
+      has<T extends keyof ESLPopupActionParams>(target: ESLPopupActionParams, p: T | symbol): boolean {
+        return Reflect.has($popup._params, p) || Reflect.has($popup, p);
+      },
+      ownKeys(target: ESLPopupActionParams): (string | symbol)[] {
+        const paramKeys = Reflect.ownKeys($popup._params);
+        const popupKeys = ($popup.constructor as typeof ESLPopup).CONFIG_KEYS
+          .filter((key: string) => !paramKeys.includes(key) && Reflect.has($popup, key as keyof ESLPopup));
+        return [...paramKeys, ...popupKeys];
+      },
+      getOwnPropertyDescriptor: (): PropertyDescriptor | undefined => ({enumerable: true, configurable: true})
+    });
+  }
 
-  protected _containerEl?: HTMLElement;
-  protected _offsetContainer: number | [number, number];
-  protected _intersectionMargin: string;
+  protected _params: ESLPopupActionParams = {};
   protected _intersectionRatio: IntersectionRatioRect = {};
   protected _updateLoopID: number;
 
@@ -134,7 +120,8 @@ export class ESLPopup extends ESLToggleable {
   /** Container element that define bounds of popups visibility */
   @memoize()
   protected get $container(): HTMLElement | undefined {
-    return this.container ? ESLTraversingQuery.first(this.container, this) as HTMLElement : this._containerEl;
+    const {container} = this.config;
+    return container ? ESLTraversingQuery.first(container, this) as HTMLElement : this.config.containerEl;
   }
 
   /** Get the size and position of the container */
@@ -157,7 +144,7 @@ export class ESLPopup extends ESLToggleable {
   /** Get offsets arrow ratio */
   @memoize()
   protected get offsetArrowRatio(): number {
-    const offset = parseNumber(this.offsetArrow, DEFAULT_OFFSET_ARROW);
+    const offset = parseNumber(this.config.offsetArrow, DEFAULT_OFFSET_ARROW);
     const offsetNormalized = Math.max(0, Math.min(offset, 100));
     const ratio = offsetNormalized / 100;
     return isRTL(this) ? 1 - ratio : ratio;
@@ -205,24 +192,7 @@ export class ESLPopup extends ESLToggleable {
     }
 
     super.onShow(params);
-
-    // TODO: change flow to use merged params unless attribute state is used in CSS
-    Object.assign(this, copyDefinedKeys({
-      position: params.position,
-      positionOrigin: params.positionOrigin,
-      behavior: params.behavior,
-      container: params.container,
-      marginArrow: params.marginArrow,
-      offsetArrow: params.offsetArrow,
-      offsetTrigger: params.offsetTrigger,
-      disableActivatorObservation: params.disableActivatorObservation
-    }));
-
-    this._extraClass = params.extraClass;
-    this._extraStyle = params.extraStyle;
-    this._containerEl = params.containerEl;
-    this._offsetContainer = params.offsetContainer || 0;
-    this._intersectionMargin = params.intersectionMargin || '0px';
+    this._params = copy(params, (key: string) => (this.constructor as typeof ESLPopup).CONFIG_KEYS.includes(key));
 
     this.style.visibility = 'hidden'; // eliminates the blinking of the popup at the previous position
 
@@ -240,6 +210,7 @@ export class ESLPopup extends ESLToggleable {
     this.beforeOnHide(params);
     super.onHide(params);
     this.afterOnHide(params);
+    this._params = {};
   }
 
   /**
@@ -249,8 +220,8 @@ export class ESLPopup extends ESLToggleable {
     this._updatePosition();
 
     this.style.visibility = 'visible';
-    this.style.cssText += this._extraStyle || '';
-    this.$$cls(this._extraClass || '', true);
+    this.style.cssText += this.config.extraStyle || '';
+    this.$$cls(this.config.extraClass || '', true);
 
     this.$$on(this._onActivatorScroll);
     this.$$on(this._onActivatorIntersection);
@@ -273,7 +244,7 @@ export class ESLPopup extends ESLToggleable {
     this._stopUpdateLoop();
 
     this.$$attr('style', '');
-    this.$$cls(this._extraClass || '', false);
+    this.$$cls(this.config.extraClass || '', false);
 
     this.$$off({group: 'observer'});
 
@@ -289,7 +260,7 @@ export class ESLPopup extends ESLToggleable {
 
   protected get intersectionOptions(): IntersectionObserverInit {
     return {
-      rootMargin: this._intersectionMargin,
+      rootMargin: this.config.intersectionMargin,
       threshold: range(9, (x) => x / 8)
     };
   }
@@ -300,7 +271,7 @@ export class ESLPopup extends ESLToggleable {
     group: 'observer',
     event: ESLIntersectionEvent.TYPE,
     target: ($popup: ESLPopup) => $popup.activator ? ESLIntersectionTarget.for($popup.activator, $popup.intersectionOptions) : [],
-    condition: ($popup: ESLPopup) => !$popup.disableActivatorObservation
+    condition: ($popup: ESLPopup) => !$popup.config.disableActivatorObservation
   })
   protected _onActivatorIntersection(event: ESLIntersectionEvent): void {
     this._intersectionRatio = {};
@@ -309,7 +280,7 @@ export class ESLPopup extends ESLToggleable {
       return;
     }
 
-    const isHorizontal = isOnHorizontalAxis(this.position);
+    const isHorizontal = isOnHorizontalAxis(this.config.position);
     const checkIntersection = (isMajorAxis: boolean, intersectionRatio: number): void => {
       if (isMajorAxis && intersectionRatio < INTERSECTION_LIMIT_FOR_ADJACENT_AXIS) this.hide();
     };
@@ -397,27 +368,28 @@ export class ESLPopup extends ESLToggleable {
   }
 
   protected get positionConfig(): PopupPositionConfig {
-    const popupRect = Rect.from(this);
-    const arrowRect = this.$arrow ? Rect.from(this.$arrow) : new Rect();
-    const triggerRect = this.activator ? Rect.from(this.activator).shift(window.scrollX, window.scrollY) : new Rect();
-    const {containerRect} = this;
+    const {$arrow, activator, containerRect, offsetArrowRatio} = this;
+    const {position, positionOrigin, behavior, marginArrow, offsetContainer, offsetTrigger} = this.config;
 
-    const innerMargin = this.offsetTrigger + arrowRect.width / 2;
+    const popupRect = Rect.from(this);
+    const arrowRect = $arrow ? Rect.from($arrow) : new Rect();
+    const triggerRect = activator ? Rect.from(activator).shift(window.scrollX, window.scrollY) : new Rect();
 
+    const innerMargin = offsetTrigger + arrowRect.width / 2;
     return {
-      position: this.position,
-      hasInnerOrigin: this.positionOrigin === 'inner',
-      behavior: this.behavior,
-      marginArrow: this.marginArrow,
-      offsetArrowRatio: this.offsetArrowRatio,
+      position,
+      hasInnerOrigin: positionOrigin === 'inner',
+      behavior,
+      marginArrow,
+      offsetArrowRatio,
       intersectionRatio: this._intersectionRatio,
       arrow: arrowRect,
       element: popupRect,
       trigger: triggerRect,
-      inner: this.positionOrigin === 'inner' ? triggerRect.shrink(innerMargin) : triggerRect.grow(innerMargin),
-      outer: (typeof this._offsetContainer === 'number') ?
-        containerRect.shrink(this._offsetContainer) :
-        containerRect.shrink(...this._offsetContainer),
+      inner: positionOrigin === 'inner' ? triggerRect.shrink(innerMargin) : triggerRect.grow(innerMargin),
+      outer: (typeof offsetContainer === 'number') ?
+        containerRect.shrink(offsetContainer) :
+        containerRect.shrink(...offsetContainer),
       isRTL: isRTL(this)
     };
   }
@@ -434,7 +406,7 @@ export class ESLPopup extends ESLToggleable {
     this.style.top = `${popup.y}px`;
     if (!this.$arrow) return;
     // set arrow position
-    const isHorizontal = isOnHorizontalAxis(this.position);
+    const isHorizontal = isOnHorizontalAxis(this.config.position);
     this.$arrow.style.left = isHorizontal ? '' : `${arrow.x}px`;
     this.$arrow.style.top = isHorizontal ? `${arrow.y}px` : '';
   }
diff --git a/src/modules/esl-share/core/esl-share-popup.ts b/src/modules/esl-share/core/esl-share-popup.ts
index 725f27c34..8fa6f44f9 100644
--- a/src/modules/esl-share/core/esl-share-popup.ts
+++ b/src/modules/esl-share/core/esl-share-popup.ts
@@ -1,10 +1,10 @@
 import {ExportNs} from '../../esl-utils/environment/export-ns';
-import {ESLPopup} from '../../esl-popup/core/esl-popup';
+import {ESLPopup} from '../../esl-popup/core';
 import {bind, listen, memoize} from '../../esl-utils/decorators';
 import {ESLShareButton} from './esl-share-button';
 import {ESLShareConfig} from './esl-share-config';
 
-import type {ESLPopupActionParams} from '../../esl-popup/core/esl-popup';
+import type {ESLPopupActionParams} from '../../esl-popup/core';
 import type {ESLShareButtonConfig} from './esl-share-config';
 
 export type {ESLSharePopupTagShape} from './esl-share-popup.shape';
@@ -16,7 +16,21 @@ function stringifyButtonsList(btns: ESLShareButtonConfig[]): string {
 export interface ESLSharePopupActionParams extends ESLPopupActionParams {
   /** list of social networks or groups of them to display */
   list?: string;
+  /** text directionality of popups content */
+  dir?: string;
+  /** language of popups text content */
+  lang?: string;
+  /** popup without arrow */
+  disableArrow?: boolean;
 }
+/** List of ESLSharePopup config keys */
+export const ESL_SHARE_POPUP_CONFIG_KEYS: (keyof ESLSharePopupActionParams)[] = [
+  ...ESLPopup.CONFIG_KEYS,
+  'list',
+  'dir',
+  'lang',
+  'disableArrow'
+] as const;
 
 /**
  * ESLSharePopup component
@@ -38,6 +52,9 @@ export class ESLSharePopup extends ESLPopup {
     hideDelay: 300
   };
 
+  /** List of config keys */
+  public static override CONFIG_KEYS: string[] = ESL_SHARE_POPUP_CONFIG_KEYS as string[];
+
   /** List of attributes to forward from the activator to the {@link ESLSharePopup} */
   public static forwardedAttrs = ['share-title', 'share-url'];
 
@@ -62,7 +79,7 @@ export class ESLSharePopup extends ESLPopup {
     this.tabIndex = 0;
   }
 
-  /** Sets initial state of the Tooltip */
+  /** Sets initial state of the popup */
   protected override setInitialState(): void {}
 
   public override onShow(params: ESLSharePopupActionParams): void {
@@ -78,7 +95,7 @@ export class ESLSharePopup extends ESLPopup {
     super.onShow(params);
   }
 
-  /** Actions to execute on Tooltip hiding. */
+  /** Actions to execute on popup hiding. */
   public override onHide(params: ESLSharePopupActionParams): void {
     super.onHide(params);
     this.parentNode === document.body && document.body.removeChild(this);
diff --git a/src/modules/esl-tooltip/core/esl-tooltip.ts b/src/modules/esl-tooltip/core/esl-tooltip.ts
index 30aadc731..fe98c8b8c 100644
--- a/src/modules/esl-tooltip/core/esl-tooltip.ts
+++ b/src/modules/esl-tooltip/core/esl-tooltip.ts
@@ -16,9 +16,14 @@ export interface ESLTooltipActionParams extends ESLPopupActionParams {
   /** tooltip without arrow */
   disableArrow?: boolean;
 }
-
-/** @deprecated alias, use {@link ESLTooltipActionParams} instead. Will be removed in v5.0.0. */
-export type TooltipActionParams = ESLTooltipActionParams;
+/** List of ESLTooltip config keys */
+export const ESL_TOOLTIP_CONFIG_KEYS: (keyof ESLTooltipActionParams)[] = [
+  ...ESLPopup.CONFIG_KEYS,
+  'text',
+  'html',
+  'dir',
+  'lang',
+  'disableArrow'] as const;
 
 @ExportNs('Tooltip')
 export class ESLTooltip extends ESLPopup {
@@ -31,6 +36,9 @@ export class ESLTooltip extends ESLPopup {
     hideDelay: 300
   };
 
+  /** List of config keys */
+  public static override CONFIG_KEYS: string[] = ESL_TOOLTIP_CONFIG_KEYS as string[];
+
   /** Shared instanse of Tooltip */
   @memoize()
   public static get sharedInstance(): ESLTooltip {


Test patch:
diff --git a/src/modules/esl-popup/test/esl-popup.config.test.ts b/src/modules/esl-popup/test/esl-popup.config.test.ts
new file mode 100644
index 000000000..3f5c54c8c
--- /dev/null
+++ b/src/modules/esl-popup/test/esl-popup.config.test.ts
@@ -0,0 +1,129 @@
+import {ESLPopup, type ESLPopupActionParams} from '../core';
+
+describe('ESLPopup: proxy logic of config', () => {
+  let $popup: ESLPopup;
+
+  ESLPopup.register();
+
+  class TestPopup1 extends ESLPopup {
+    static override is = 'test-popup-1';
+    static override DEFAULT_PARAMS = {};
+  }
+  TestPopup1.register();
+
+  class TestPopup2 extends ESLPopup {
+    static override is = 'test-popup-2';
+    static override DEFAULT_PARAMS: ESLPopupActionParams = {position: 'right'};
+  }
+  TestPopup2.register();
+
+  const defaultAttrs: ESLPopupActionParams = {
+    position: 'top',
+    positionOrigin: 'outer',
+    behavior: 'fit',
+    marginArrow: 5,
+    offsetArrow: '50',
+    offsetTrigger: 3,
+    container: '',
+    disableActivatorObservation: false
+  };
+
+  const defaultParams: ESLPopupActionParams = {
+    intersectionMargin: '0px',
+    offsetContainer: 15
+  };
+
+  beforeEach(() => {
+    $popup = new ESLPopup();
+  });
+  afterEach(() => {
+    $popup.hide();
+  });
+
+  test('should return default attributes if the showing parameters and default parameters are missing', () => {
+    $popup = new TestPopup1();
+    $popup.show({});
+    expect({...$popup.config}).toEqual({...defaultAttrs});
+  });
+
+  test('should return merging default parameters and default attributes if the showing parameters are missing', () => {
+    $popup.show({});
+    expect({...$popup.config}).toEqual({...defaultAttrs, ...defaultParams});
+  });
+
+  test('should return merging showing parameters, default parameters, and default attributes', () => {
+    const params: ESLPopupActionParams = {
+      extraClass: 'test-class'
+    };
+    $popup.show(params);
+    expect({...$popup.config}).toEqual({...defaultAttrs, ...defaultParams, ...params});
+  });
+
+  test('should have value from default params in the case also prop defined via attributes', () => {
+    $popup = new TestPopup2();
+    $popup.show({});
+    expect($popup.config.position).toBe('right');
+  });
+
+  test('should have value from the passed onShow() params in the case also prop defined both via default and attributes', () => {
+    $popup = new TestPopup2();
+    $popup.show({position: 'left'});
+    expect($popup.config.position).toBe('left');
+  });
+
+  test('should prohibit the ability to change configuration properties', () => {
+    const setProperty = () => {
+      $popup.config.container = 'test-value';
+    };
+    expect(setProperty).toThrow('\'set\' on proxy: trap returned falsish for property \'container\'');
+  });
+
+  test('should prohibit the ability to add new configuration properties', () => {
+    const setProperty = () => {
+      $popup.config.foo = 'bar';
+    };
+    expect(setProperty).toThrow('\'set\' on proxy: trap returned falsish for property \'foo\'');
+  });
+
+  test('should have enumerable properties', () => {
+    $popup.show({});
+    const entries = Object.entries($popup.config);
+    expect(entries).toEqual(expect.arrayContaining([
+      ...Object.entries(defaultParams),
+      ...Object.entries(defaultAttrs)
+    ]));
+  });
+
+  test('should return default attributes after popup hiding', () => {
+    const params: ESLPopupActionParams = {
+      extraClass: 'test-class'
+    };
+    $popup.show(params);
+    $popup.hide();
+    expect({...$popup.config}).toEqual({...defaultAttrs});
+  });
+
+  test('should be updated after each onShow() call', () => {
+    const params1: ESLPopupActionParams = {
+      extraClass: 'test-class'
+    };
+    const params2: ESLPopupActionParams = {
+      extraStyle: 'color: red'
+    };
+    $popup.show(params1);
+    expect({...$popup.config}).toEqual({...defaultAttrs, ...defaultParams, ...params1});
+    $popup.hide();
+    $popup.show(params2);
+    expect({...$popup.config}).toEqual({...defaultAttrs, ...defaultParams, ...params2});
+  });
+
+  test('should be updated after attribure changing', () => {
+    const params: ESLPopupActionParams = {
+      extraClass: 'test-class'
+    };
+    $popup.show(params);
+    expect({...$popup.config}).toEqual({...defaultAttrs, ...defaultParams, ...params});
+    $popup.container = '::prev';
+    expect({...$popup.config}).toEqual({...defaultAttrs, container: '::prev', ...defaultParams, ...params});
+  });
+});
diff --git a/src/modules/esl-popup/test/esl-popup.position-config.test.ts b/src/modules/esl-popup/test/esl-popup.position-config.test.ts
index 9765af993..0b29cf4e7 100644
--- a/src/modules/esl-popup/test/esl-popup.position-config.test.ts
+++ b/src/modules/esl-popup/test/esl-popup.position-config.test.ts
@@ -1,4 +1,4 @@
-import {ESLPopup, type ESLPopupActionParams} from '../core/esl-popup';
+import {ESLPopup, type ESLPopupActionParams} from '../core';
 import {Rect} from '../../esl-utils/dom/rect';
 import {createPopupMock, createDimensional} from './popup.mock';