Repository: exadel-inc/esl
Pull request #2126 (2023-12-18 22:54:27)

fix: esl-popup, esl-tooltip, esl-toggleable hide flow; deprecation of incorrect types; esl-select fixes


Patch:
diff --git a/.releaserc.yml b/.releaserc.yml
index 65cd75a28..4ce44e132 100644
--- a/.releaserc.yml
+++ b/.releaserc.yml
@@ -70,7 +70,9 @@ plugins:
 
   - - "@semantic-release/github"
     - assets:
-        - path: ./*.tgz
-          label: Released NPM Tarballs
+        - path: ./exadel-esl-*.tgz
+          label: Released Core NPM Tarball
+        - path: ./exadel-eslint-plugin-esl-*.tgz
+          label: Released ESLint NPM Tarball
         - path: CHANGELOG.md
           label: Changelog
diff --git a/eslint/README.md b/eslint/README.md
index 73cce4f3e..c1f75b71b 100644
--- a/eslint/README.md
+++ b/eslint/README.md
@@ -54,42 +54,15 @@ However, you still have the option to manually manage the rules if needed.
 
 The ESLint plugin provides a separate rule for each deprecated utility within the ESL project, that's considered to be deprecated. Below is the list of them:
 
-- `@exadel/esl/deprecated-4/generate-uid` - Rule for deprecated `generateUId` alias for [`randUID`](https://github.com/exadel-inc/esl/blob/417eb781a99cd789b43e893a24540ea7ae831141/src/modules/esl-utils/misc/uid.ts#L20). Can be set to `warn` or `error`.
-- `@exadel/esl/deprecated-4/deep-compare` - Rule for deprecated `deepCompare` alias for [`isEqual`](https://github.com/exadel-inc/esl/blob/417eb781a99cd789b43e893a24540ea7ae831141/src/modules/esl-utils/misc/object/compare.ts#L4). Can be set to `warn` or `error`.
-- `@exadel/esl/deprecated-4/event-utils` - Rule for deprecated `EventUtils` alias for [`ESLEventUtils`](https://github.com/exadel-inc/esl/blob/417eb781a99cd789b43e893a24540ea7ae831141/src/modules/esl-event-listener/core/api.ts#L13). Can be set to `warn` or `error`.
-- `@exadel/esl/deprecated-4/traversing-query` - Rule for deprecated `TraversingQuery` alias for [`ESLTraversingQuery`](https://github.com/exadel-inc/esl/blob/417eb781a99cd789b43e893a24540ea7ae831141/src/modules/esl-traversing-query/core/esl-traversing-query.ts#L40). Can be set to `warn` or `error`.
-- `@exadel/esl/deprecated-4/toggleable-action-params` - Rule for deprecated `ToggleableActionParams` alias for [`ESLToggleableActionParams`](https://github.com/exadel-inc/esl/blob/417eb781a99cd789b43e893a24540ea7ae831141/src/modules/esl-toggleable/core/esl-toggleable.ts#L15). Can be set to `warn` or `error`.
+- `@exadel/esl/deprecated-4/alert-action-params` - Rule for deprecated `AlertActionParams` alias for `ESLAlertActionParams`.
+- `@exadel/esl/deprecated-4/generate-uid` - Rule for deprecated `generateUId` alias for `randUID`.
+- `@exadel/esl/deprecated-4/deep-compare` - Rule for deprecated `deepCompare` alias for `isEqual`.
+- `@exadel/esl/deprecated-4/event-utils` - Rule for deprecated `EventUtils` alias for `ESLEventUtils`.
+- `@exadel/esl/deprecated-4/panel-action-params` - Rule for deprecated `PanelActionParams` alias for `ESLPanelActionParams`.
+- `@exadel/esl/deprecated-4/popup-action-params` - Rule for deprecated `PopupActionParams` alias for `ESLPopupActionParams`.
+- `@exadel/esl/deprecated-4/traversing-query` - Rule for deprecated `TraversingQuery` alias for `ESLTraversingQuery`.
+- `@exadel/esl/deprecated-4/toggleable-action-params` - Rule for deprecated `ToggleableActionParams` alias for `ESLToggleableActionParams`.
+- `@exadel/esl/deprecated-4/tooltip-action-params` - Rule for deprecated `TooltipActionParams` alias for `ESLTooltipActionParams`.
 
-These rules can be configured manually inside the `rules` section of your ESLint configuration file.
-
-For example, previously mentioned `plugin:@exadel/esl/default-4` preset is equal to the following rules configuration:
-
-```json
-{
-  // ...
-  "rules": {
-    "@exadel/esl/deprecated-4/generate-uid" : "warn",
-    "@exadel/esl/deprecated-4/deep-compare" : "warn",
-    "@exadel/esl/deprecated-4/event-utils" : "warn",
-    "@exadel/esl/deprecated-4/traversing-query" : "warn",
-    "@exadel/esl/deprecated-4/toggleable-action-params" : "warn"
-  }
-  // ...
-}
-```
 
-And `plugin:@exadel/esl/default-5` is equal to:
-
-```json
-{
-  // ...
-  "rules": {
-    "@exadel/esl/deprecated-4/generate-uid" : "error",
-    "@exadel/esl/deprecated-4/deep-compare" : "error",
-    "@exadel/esl/deprecated-4/event-utils" : "error",
-    "@exadel/esl/deprecated-4/traversing-query" : "error",
-    "@exadel/esl/deprecated-4/toggleable-action-params" : "error"
-  }
-  // ...
-}
-```
+These rules can be configured manually inside the `rules` section of your ESLint configuration file.
diff --git a/eslint/src/index.ts b/eslint/src/index.ts
index 070b75fa5..4619ef794 100644
--- a/eslint/src/index.ts
+++ b/eslint/src/index.ts
@@ -1,19 +1,27 @@
-import generateUID from './rules/4/deprecated.generate-uid';
-import deepCompare from './rules/4/deprecated.deep-compare';
-import eventUtils from './rules/4/deprecated.event-utils';
-import traversingQuery from './rules/4/deprecated.traversing-query';
-import toggleableParams from './rules/4/deprecated.toggleable-action-params';
+import deprecatedAlertActionParams from './rules/4/deprecated.alert-action-params';
+import deprecatedGenerateUid from './rules/4/deprecated.generate-uid';
+import deprecatedDeepCompare from './rules/4/deprecated.deep-compare';
+import deprecatedEventUtils from './rules/4/deprecated.event-utils';
+import deprecatedPanelActionParams from './rules/4/deprecated.panel-action-params';
+import deprecatedPopupActionParams from './rules/4/deprecated.popup-action-params';
+import deprecatedTraversingQuery from './rules/4/deprecated.traversing-query';
+import deprecatedToggleableActionParams from './rules/4/deprecated.toggleable-action-params';
+import deprecatedTooltipActionParams from './rules/4/deprecated.tooltip-action-params';
 
 import type {Rule} from 'eslint';
 
 export type logLevel = 'warn' | 'error';
 
 const DEPRECATED_4_RULES = {
-  'deprecated-4/generate-uid': generateUID,
-  'deprecated-4/deep-compare': deepCompare,
-  'deprecated-4/event-utils': eventUtils,
-  'deprecated-4/traversing-query': traversingQuery,
-  'deprecated-4/toggleable-action-params': toggleableParams
+  'deprecated-4/alert-action-params': deprecatedAlertActionParams,
+  'deprecated-4/generate-uid': deprecatedGenerateUid,
+  'deprecated-4/deep-compare': deprecatedDeepCompare,
+  'deprecated-4/event-utils': deprecatedEventUtils,
+  'deprecated-4/panel-action-params': deprecatedPanelActionParams,
+  'deprecated-4/popup-action-params': deprecatedPopupActionParams,
+  'deprecated-4/traversing-query': deprecatedTraversingQuery,
+  'deprecated-4/toggleable-action-params': deprecatedToggleableActionParams,
+  'deprecated-4/tooltip-action-params': deprecatedTooltipActionParams
 };
 
 const buildDefault = (definition: Record<string, Rule.RuleModule>, level: logLevel): Record<string, logLevel> => {
diff --git a/eslint/src/rules/4/deprecated.alert-action-params.ts b/eslint/src/rules/4/deprecated.alert-action-params.ts
new file mode 100644
index 000000000..62b0a87d2
--- /dev/null
+++ b/eslint/src/rules/4/deprecated.alert-action-params.ts
@@ -0,0 +1,9 @@
+import {buildRule} from '../../core/deprecated-alias';
+
+/**
+ * Rule for deprecated `AlertActionParams` alias for {@link ESLAlertActionParams}
+ */
+export default buildRule({
+  alias: 'ESLAlertActionParams',
+  deprecation: 'AlertActionParams'
+});
diff --git a/eslint/src/rules/4/deprecated.deep-compare.ts b/eslint/src/rules/4/deprecated.deep-compare.ts
index 95f6a0092..a57dcd1a7 100644
--- a/eslint/src/rules/4/deprecated.deep-compare.ts
+++ b/eslint/src/rules/4/deprecated.deep-compare.ts
@@ -5,5 +5,5 @@ import {buildRule} from '../../core/deprecated-alias';
  */
 export default buildRule({
   alias: 'isEqual',
-  deprecation: 'deepCompare',
+  deprecation: 'deepCompare'
 });
diff --git a/eslint/src/rules/4/deprecated.event-utils.ts b/eslint/src/rules/4/deprecated.event-utils.ts
index a81ccfed7..1401bd66e 100644
--- a/eslint/src/rules/4/deprecated.event-utils.ts
+++ b/eslint/src/rules/4/deprecated.event-utils.ts
@@ -5,5 +5,5 @@ import {buildRule} from '../../core/deprecated-alias';
  */
 export default buildRule({
   alias: 'ESLEventUtils',
-  deprecation: 'EventUtils',
+  deprecation: 'EventUtils'
 });
diff --git a/eslint/src/rules/4/deprecated.generate-uid.ts b/eslint/src/rules/4/deprecated.generate-uid.ts
index e12a81b94..36b881d67 100644
--- a/eslint/src/rules/4/deprecated.generate-uid.ts
+++ b/eslint/src/rules/4/deprecated.generate-uid.ts
@@ -5,5 +5,5 @@ import {buildRule} from '../../core/deprecated-alias';
  */
 export default buildRule({
   alias: 'randUID',
-  deprecation: 'generateUId',
+  deprecation: 'generateUId'
 });
diff --git a/eslint/src/rules/4/deprecated.panel-action-params.ts b/eslint/src/rules/4/deprecated.panel-action-params.ts
new file mode 100644
index 000000000..1d4424fe4
--- /dev/null
+++ b/eslint/src/rules/4/deprecated.panel-action-params.ts
@@ -0,0 +1,9 @@
+import {buildRule} from '../../core/deprecated-alias';
+
+/**
+ * Rule for deprecated `PanelActionParams` alias for {@link ESLPanelActionParams}
+ */
+export default buildRule({
+  alias: 'ESLPanelActionParams',
+  deprecation: 'PanelActionParams'
+});
diff --git a/eslint/src/rules/4/deprecated.popup-action-params.ts b/eslint/src/rules/4/deprecated.popup-action-params.ts
new file mode 100644
index 000000000..d4e5db13d
--- /dev/null
+++ b/eslint/src/rules/4/deprecated.popup-action-params.ts
@@ -0,0 +1,9 @@
+import {buildRule} from '../../core/deprecated-alias';
+
+/**
+ * Rule for deprecated `PopupActionParams` alias for {@link ESLPopupActionParams}
+ */
+export default buildRule({
+  alias: 'ESLPopupActionParams',
+  deprecation: 'PopupActionParams'
+});
diff --git a/eslint/src/rules/4/deprecated.toggleable-action-params.ts b/eslint/src/rules/4/deprecated.toggleable-action-params.ts
index 888b342d6..008934aa7 100644
--- a/eslint/src/rules/4/deprecated.toggleable-action-params.ts
+++ b/eslint/src/rules/4/deprecated.toggleable-action-params.ts
@@ -5,5 +5,5 @@ import {buildRule} from '../../core/deprecated-alias';
  */
 export default buildRule({
   alias: 'ESLToggleableActionParams',
-  deprecation: 'ToggleableActionParams',
+  deprecation: 'ToggleableActionParams'
 });
diff --git a/eslint/src/rules/4/deprecated.tooltip-action-params.ts b/eslint/src/rules/4/deprecated.tooltip-action-params.ts
new file mode 100644
index 000000000..e4c9b0bcd
--- /dev/null
+++ b/eslint/src/rules/4/deprecated.tooltip-action-params.ts
@@ -0,0 +1,10 @@
+import {buildRule} from '../../core/deprecated-alias';
+
+/**
+ * Rule for deprecated `TooltipActionParams` alias for {@link ESLTooltipActionParams}
+ */
+export default buildRule({
+  alias: 'ESLTooltipActionParams',
+  deprecation: 'TooltipActionParams'
+});
+
diff --git a/eslint/src/rules/4/deprecated.traversing-query.ts b/eslint/src/rules/4/deprecated.traversing-query.ts
index 7255d736b..a1ff52527 100644
--- a/eslint/src/rules/4/deprecated.traversing-query.ts
+++ b/eslint/src/rules/4/deprecated.traversing-query.ts
@@ -5,5 +5,5 @@ import {buildRule} from '../../core/deprecated-alias';
  */
 export default buildRule({
   alias: 'ESLTraversingQuery',
-  deprecation: 'TraversingQuery',
+  deprecation: 'TraversingQuery'
 });
diff --git a/pages/views/examples/footnotes.njk b/pages/views/examples/footnotes.njk
index 0cfafa6f7..ba6187b68 100644
--- a/pages/views/examples/footnotes.njk
+++ b/pages/views/examples/footnotes.njk
@@ -150,7 +150,7 @@ aside:
     <h3>Simple Menu:</h3>
     <ul class="esl-example-menu mb-4">
       <li class="menu-item">
-        <esl-trigger class="menu-item-btn menu-item-arrow">Menu 1</esl-trigger>
+        <esl-trigger target="::next" class="menu-item-btn menu-item-arrow">Menu 1</esl-trigger>
         <esl-panel class="menu-container"
                    group="test-nav-group"
                    fallback-duration="400"
@@ -163,7 +163,7 @@ aside:
         </esl-panel>
       </li>
       <li class="menu-item">
-        <esl-trigger class="menu-item-btn menu-item-arrow">Menu 2</esl-trigger>
+        <esl-trigger target="::next" class="menu-item-btn menu-item-arrow">Menu 2</esl-trigger>
         <esl-panel class="menu-container"
                    group="test-nav-group"
                    fallback-duration="400"
@@ -172,7 +172,7 @@ aside:
         </esl-panel>
       </li>
       <li class="menu-item">
-        <esl-trigger class="menu-item-btn menu-item-arrow">Menu 3</esl-trigger>
+        <esl-trigger target="::next" class="menu-item-btn menu-item-arrow">Menu 3</esl-trigger>
         <esl-panel class="menu-container"
                    group="test-nav-group"
                    fallback-duration="400"
@@ -189,7 +189,7 @@ aside:
     <h3>Accordion with notes</h3>
     <esl-panel-group class="accordion-group mb-4">
       <div class="esl-d-accordion">
-        <esl-trigger class="esl-d-accordion-header h4 esl-d-accordion-header-arrow" target="::next">
+        <esl-trigger target="::next" class="esl-d-accordion-header h4 esl-d-accordion-header-arrow">
           Toggle Accordion #1
         </esl-trigger>
         <esl-panel open class="esl-d-accordion-panel">
@@ -204,7 +204,7 @@ aside:
         </esl-panel>
       </div>
       <div class="esl-d-accordion">
-        <esl-trigger class="esl-d-accordion-header h4 esl-d-accordion-header-arrow" target="::next">
+        <esl-trigger target="::next" class="esl-d-accordion-header h4 esl-d-accordion-header-arrow">
           Toggle Accordion #2
         </esl-trigger>
         <esl-panel class="esl-d-accordion-panel">
@@ -220,7 +220,7 @@ aside:
         </esl-panel>
       </div>
       <div class="esl-d-accordion">
-        <esl-trigger class="esl-d-accordion-header h4 esl-d-accordion-header-arrow" target="::next">
+        <esl-trigger target="::next" class="esl-d-accordion-header h4 esl-d-accordion-header-arrow">
           Toggle Accordion #3
         </esl-trigger>
         <esl-panel class="esl-d-accordion-panel">
diff --git a/pages/views/examples/forms-select-list.njk b/pages/views/examples/forms-select-list.njk
index bb88db873..afdef7065 100644
--- a/pages/views/examples/forms-select-list.njk
+++ b/pages/views/examples/forms-select-list.njk
@@ -166,7 +166,7 @@ aside:
                 <option>Paraguay</option>
                 <option>Peru</option>
                 <option>Philippines</option>
-                <option>Poland</option>
+                <option selected>Poland</option>
                 <option>Portugal</option>
                 <option>Qatar</option>
                 <option>Romania</option>
diff --git a/pages/views/examples/forms-select.njk b/pages/views/examples/forms-select.njk
index 2e610802c..6ab65c14b 100644
--- a/pages/views/examples/forms-select.njk
+++ b/pages/views/examples/forms-select.njk
@@ -168,7 +168,7 @@ aside:
               <option>Paraguay</option>
               <option>Peru</option>
               <option>Philippines</option>
-              <option>Poland</option>
+              <option selected>Poland</option>
               <option>Portugal</option>
               <option>Qatar</option>
               <option>Romania</option>
diff --git a/src/modules/esl-alert/core/esl-alert.ts b/src/modules/esl-alert/core/esl-alert.ts
index b60f40e3e..559145b07 100644
--- a/src/modules/esl-alert/core/esl-alert.ts
+++ b/src/modules/esl-alert/core/esl-alert.ts
@@ -9,7 +9,7 @@ import {ESLTraversingQuery} from '../../esl-traversing-query/core';
 
 import type {ESLToggleableActionParams, ESLToggleableRequestDetails} from '../../esl-toggleable/core';
 
-export interface AlertActionParams extends ESLToggleableRequestDetails {
+export interface ESLAlertActionParams extends ESLToggleableRequestDetails {
   /** text to be shown; passes empty string or null to hide */
   text?: string;
   /** html content */
@@ -20,6 +20,9 @@ export interface AlertActionParams 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)
@@ -35,10 +38,10 @@ export class ESLAlert extends ESLToggleable {
    * Legacy default show/hide params for all ESLAlert instances
    * @deprecated Use {@link ESLAlert.DEFAULT_PARAMS} instead
    */
-  public static defaultConfig: AlertActionParams = {};
+  public static defaultConfig: ESLAlertActionParams = {};
 
   /** Default show/hide params for all ESLAlert instances */
-  public static override DEFAULT_PARAMS: AlertActionParams = {
+  public static override DEFAULT_PARAMS: ESLAlertActionParams = {
     hideTime: 300,
     hideDelay: 2500
   };
@@ -55,8 +58,8 @@ export class ESLAlert extends ESLToggleable {
   @attr({defaultValue: '::parent'}) public target: string;
 
   /** Default show/hide params for current ESLAlert instance */
-  @jsonAttr<AlertActionParams>()
-  public override defaultParams: AlertActionParams;
+  @jsonAttr<ESLAlertActionParams>()
+  public override defaultParams: ESLAlertActionParams;
 
   protected $content: HTMLElement;
   protected activeCls?: string;
@@ -109,7 +112,7 @@ export class ESLAlert extends ESLToggleable {
     this.$$on(this._onHideRequest);
   }
 
-  protected override onShow(params: AlertActionParams): void {
+  protected override onShow(params: ESLAlertActionParams): void {
     if (this._clearTimeout) window.clearTimeout(this._clearTimeout);
     if (params.html || params.text) {
       this.render(params);
@@ -117,12 +120,12 @@ export class ESLAlert extends ESLToggleable {
     }
     this.hide(params);
   }
-  protected override onHide(params: AlertActionParams): void {
+  protected override onHide(params: ESLAlertActionParams): void {
     super.onHide(params);
     this._clearTimeout = window.setTimeout(() => this.clear(), params.hideTime);
   }
 
-  protected render({text, html, cls}: AlertActionParams): void {
+  protected render({text, html, cls}: ESLAlertActionParams): void {
     CSSClassUtils.remove(this, this.activeCls);
     CSSClassUtils.add(this, this.activeCls = cls);
     if (html) this.$content.innerHTML = html;
@@ -133,7 +136,7 @@ export class ESLAlert extends ESLToggleable {
     CSSClassUtils.remove(this, this.activeCls);
   }
 
-  protected override buildRequestParams(e: CustomEvent<ESLToggleableRequestDetails>): AlertActionParams | null {
+  protected override buildRequestParams(e: CustomEvent<ESLToggleableRequestDetails>): ESLAlertActionParams | null {
     const detail = e.detail || {};
     if (!isMatches(this, detail.match)) return null;
     if (e.type === this.SHOW_REQUEST_EVENT) return Object.assign({}, detail, {force: true});
diff --git a/src/modules/esl-event-listener/core/targets/wheel.target.ts b/src/modules/esl-event-listener/core/targets/wheel.target.ts
index 402a77510..468555f3f 100644
--- a/src/modules/esl-event-listener/core/targets/wheel.target.ts
+++ b/src/modules/esl-event-listener/core/targets/wheel.target.ts
@@ -99,7 +99,7 @@ export class ESLWheelTarget extends SyntheticEventTarget {
 
   /**
    * Calculates the scroll pixels for a given wheel event
-   * @param events - An event to retrieve scroll value from
+   * @param event - An event to retrieve scroll value from
    * @param isVertical - A boolean indicating the axis (vertical or horizontal) for scroll calculation
    * @returns The number of pixels scrolled
    */
@@ -123,7 +123,7 @@ export class ESLWheelTarget extends SyntheticEventTarget {
 
   /** Subscribes to wheel event */
   public override addEventListener(callback: EventListener): void;
-  public override addEventListener(event: string, callback: EventListener): void;
+  public override addEventListener(event: typeof ESLWheelEvent.type, callback: EventListener): void;
   public override addEventListener(event: any, callback: EventListener = event): void {
     super.addEventListener(event, callback);
 
@@ -133,7 +133,7 @@ export class ESLWheelTarget extends SyntheticEventTarget {
 
   /** Unsubscribes from the observed target {@link Element} wheel events */
   public override removeEventListener(callback: EventListener): void;
-  public override removeEventListener(event: string, callback: EventListener): void;
+  public override removeEventListener(event: typeof ESLWheelEvent.type, callback: EventListener): void;
   public override removeEventListener(event: any, callback: EventListener = event): void {
     super.removeEventListener(event, callback);
 
diff --git a/src/modules/esl-footnotes/core/esl-note.ts b/src/modules/esl-footnotes/core/esl-note.ts
index c4cb063fb..36f5c5fe5 100644
--- a/src/modules/esl-footnotes/core/esl-note.ts
+++ b/src/modules/esl-footnotes/core/esl-note.ts
@@ -11,7 +11,7 @@ import {ESLMediaQuery} from '../../esl-media-query/core';
 import {ESLTraversingQuery} from '../../esl-traversing-query/core';
 
 import type {ESLFootnotes} from './esl-footnotes';
-import type {TooltipActionParams} from '../../esl-tooltip/core/esl-tooltip';
+import type {ESLTooltipActionParams} from '../../esl-tooltip/core/esl-tooltip';
 import type {IMediaQueryCondition} from '../../esl-media-query/core/conditions/media-query-base';
 
 @ExportNs('Note')
@@ -214,7 +214,7 @@ export class ESLNote extends ESLBaseElement {
   }
 
   /** Merge params to pass to the toggleable */
-  protected mergeToggleableParams(this: ESLNote, ...params: TooltipActionParams[]): TooltipActionParams {
+  protected mergeToggleableParams(this: ESLNote, ...params: ESLTooltipActionParams[]): ESLTooltipActionParams {
     const container = this.getClosestRelatedAttr('container') || this.container;
     const containerEl = container ? ESLTraversingQuery.first(container, this) as HTMLElement : undefined;
     return Object.assign({
@@ -229,7 +229,7 @@ export class ESLNote extends ESLBaseElement {
   }
 
   /** Shows tooltip with passed params */
-  public showTooltip(params: TooltipActionParams = {}): void {
+  public showTooltip(params: ESLTooltipActionParams = {}): void {
     const actionParams = this.mergeToggleableParams({
     }, params);
     if (ESLTooltip.open) {
@@ -239,13 +239,13 @@ export class ESLNote extends ESLBaseElement {
     this.highlight();
   }
   /** Hides tooltip with passed params */
-  public hideTooltip(params: TooltipActionParams = {}): void {
+  public hideTooltip(params: ESLTooltipActionParams = {}): void {
     const actionParams = this.mergeToggleableParams({
     }, params);
     ESLTooltip.hide(actionParams);
   }
   /** Toggles tooltip with passed params */
-  public toggleTooltip(params: TooltipActionParams = {}, state: boolean = !this.tooltipShown): void {
+  public toggleTooltip(params: ESLTooltipActionParams = {}, state: boolean = !this.tooltipShown): void {
     state ? this.showTooltip(params) : this.hideTooltip(params);
   }
 
diff --git a/src/modules/esl-forms/esl-select/core/esl-select-dropdown.ts b/src/modules/esl-forms/esl-select/core/esl-select-dropdown.ts
index e3e4699cb..2a8e05a7e 100644
--- a/src/modules/esl-forms/esl-select/core/esl-select-dropdown.ts
+++ b/src/modules/esl-forms/esl-select/core/esl-select-dropdown.ts
@@ -68,11 +68,13 @@ export class ESLSelectDropdown extends ESLPopup {
     $select && setTimeout(() => $select.focus({preventScroll: true}), 0);
   }
 
-  protected override afterOnHide(): void {
-    this._disposeTimeout = window.setTimeout(() => {
-      super.afterOnHide();
+  protected override afterOnHide(params: ESLToggleableActionParams): void {
+    const afterOnHideTask = (): void => {
+      super.afterOnHide(params);
       if (this.parentElement === document.body) document.body.removeChild(this);
-    }, 1000);
+    };
+    if (params.action === 'show') afterOnHideTask();
+    else this._disposeTimeout = window.setTimeout(afterOnHideTask, 1000);
   }
 
   @listen('keydown')
diff --git a/src/modules/esl-panel-group/core/esl-panel-group.ts b/src/modules/esl-panel-group/core/esl-panel-group.ts
index 240f61b2a..a95a76903 100644
--- a/src/modules/esl-panel-group/core/esl-panel-group.ts
+++ b/src/modules/esl-panel-group/core/esl-panel-group.ts
@@ -10,7 +10,7 @@ import {ESLMediaQuery, ESLMediaRuleList} from '../../esl-media-query/core';
 import {ESLTraversingQuery} from '../../esl-traversing-query/core';
 import {ESLPanel} from '../../esl-panel/core';
 
-import type {PanelActionParams} from '../../esl-panel/core';
+import type {ESLPanelActionParams} from '../../esl-panel/core';
 
 /** Converts special 'all' value to positive infinity */
 const parseCount = (value: string): number => value === 'all' ? Number.POSITIVE_INFINITY : parseInt(value, 10);
@@ -67,7 +67,7 @@ export class ESLPanelGroup extends ESLBaseElement {
   @attr({defaultValue: 'last'}) public refreshStrategy: string;
 
   /** Action params to pass into panels when executing reset action (happens when mode is changed) */
-  @jsonAttr({defaultValue: {noAnimate: true}}) public transformParams: PanelActionParams;
+  @jsonAttr({defaultValue: {noAnimate: true}}) public transformParams: ESLPanelActionParams;
 
   /** Readonly attribute that indicates whether the panel group has opened panels */
   @boolAttr({readonly: true}) public hasOpened: boolean;
@@ -203,7 +203,7 @@ export class ESLPanelGroup extends ESLBaseElement {
   }
 
   /** @returns action params config that's used (inherited) by controlled {@link ESLPanel}s */
-  public get panelConfig(): PanelActionParams {
+  public get panelConfig(): ESLPanelActionParams {
     return {
       capturedBy: this.currentMode === 'tabs' ? this : undefined,
       noAnimate: !this.shouldAnimate || (this.currentMode === 'tabs')
@@ -211,7 +211,7 @@ export class ESLPanelGroup extends ESLBaseElement {
   }
 
   /** @returns merged panel action params for show/hide requests from the group */
-  protected mergeActionParams(...params: PanelActionParams[]): PanelActionParams {
+  protected mergeActionParams(...params: ESLPanelActionParams[]): ESLPanelActionParams {
     return Object.assign({initiator: 'group', activator: this}, ...params);
   }
 
@@ -222,11 +222,11 @@ export class ESLPanelGroup extends ESLBaseElement {
   }
 
   /** Shows all panels besides excluded ones */
-  public showAll(excluded: ESLPanel[] = [], params: PanelActionParams = {}): void {
+  public showAll(excluded: ESLPanel[] = [], params: ESLPanelActionParams = {}): void {
     this.$panels.forEach((el) => !excluded.includes(el) && el.show(this.mergeActionParams(params)));
   }
   /** Hides all active panels besides excluded ones */
-  public hideAll(excluded: ESLPanel[] = [], params: PanelActionParams = {}): void {
+  public hideAll(excluded: ESLPanel[] = [], params: ESLPanelActionParams = {}): void {
     this.$activePanels.forEach((el) => !excluded.includes(el) && el.hide(this.mergeActionParams(params)));
   }
 
diff --git a/src/modules/esl-panel/core/esl-panel.ts b/src/modules/esl-panel/core/esl-panel.ts
index c2b354f21..cc759f4c9 100644
--- a/src/modules/esl-panel/core/esl-panel.ts
+++ b/src/modules/esl-panel/core/esl-panel.ts
@@ -8,13 +8,16 @@ import type {ESLPanelGroup} from '../../esl-panel-group/core';
 import type {ESLToggleableActionParams} from '../../esl-toggleable/core';
 
 /** {@link ESLPanel} action params interface */
-export interface PanelActionParams extends ESLToggleableActionParams {
+export interface ESLPanelActionParams extends ESLToggleableActionParams {
   /** Panel group */
   capturedBy?: ESLPanelGroup;
   /** Prevents collapsing/expanding animation */
   noAnimate?: boolean;
 }
 
+/** @deprecated alias, use {@link ESLPanelActionParams} instead. Will be removed in v5.0.0. */
+export type PanelActionParams = ESLPanelActionParams;
+
 /**
  * ESLPanel component
  * @author Julia Murashko
@@ -38,8 +41,8 @@ export class ESLPanel extends ESLToggleable {
   @attr({defaultValue: 'esl-panel-group'}) public panelGroupSel: string;
 
   /** Initial params for current ESLPanel instance */
-  @jsonAttr<PanelActionParams>({defaultValue: {force: true, initiator: 'init'}})
-  public override initialParams: PanelActionParams;
+  @jsonAttr<ESLPanelActionParams>({defaultValue: {force: true, initiator: 'init'}})
+  public override initialParams: ESLPanelActionParams;
 
   /** Active while animation in progress */
   @boolAttr({readonly: true}) public animating: boolean;
@@ -59,7 +62,7 @@ export class ESLPanel extends ESLToggleable {
   }
 
   /** Process show action */
-  protected override onShow(params: PanelActionParams): void {
+  protected override onShow(params: ESLPanelActionParams): void {
     this._initialHeight = this.scrollHeight;
     super.onShow(params);
 
@@ -69,7 +72,7 @@ export class ESLPanel extends ESLToggleable {
   }
 
   /** Process hide action */
-  protected override onHide(params: PanelActionParams): void {
+  protected override onHide(params: ESLPanelActionParams): void {
     this._initialHeight = this.scrollHeight;
     super.onHide(params);
 
diff --git a/src/modules/esl-popup/core/esl-popup.ts b/src/modules/esl-popup/core/esl-popup.ts
index ecfcfd6a4..a17ef601d 100644
--- a/src/modules/esl-popup/core/esl-popup.ts
+++ b/src/modules/esl-popup/core/esl-popup.ts
@@ -20,7 +20,7 @@ import type {PositionType, IntersectionRatioRect} from './esl-popup-position';
 const INTERSECTION_LIMIT_FOR_ADJACENT_AXIS = 0.7;
 const DEFAULT_OFFSET_ARROW = 50;
 
-export interface PopupActionParams extends ESLToggleableActionParams {
+export interface ESLPopupActionParams extends ESLToggleableActionParams {
   /** popup position relative to trigger */
   position?: PositionType;
   /** popup behavior if it does not fit in the window */
@@ -45,6 +45,8 @@ export interface PopupActionParams extends ESLToggleableActionParams {
   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;
+  /** Autofocus on popup/activator */
+  autofocus?: boolean;
 
   /** Extra class to add to popup on activation */
   extraClass?: string;
@@ -52,12 +54,15 @@ export interface PopupActionParams extends ESLToggleableActionParams {
   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';
 
   /** Default params to pass into the popup on show/hide actions */
-  public static override DEFAULT_PARAMS: PopupActionParams = {
+  public static override DEFAULT_PARAMS: ESLPopupActionParams = {
     offsetTrigger: 3,
     offsetContainer: 15,
     intersectionMargin: '0px'
@@ -96,8 +101,8 @@ export class ESLPopup extends ESLToggleable {
   @attr() public container: string;
 
   /** Default show/hide params for current ESLAlert instance */
-  @jsonAttr<PopupActionParams>()
-  public override defaultParams: PopupActionParams;
+  @jsonAttr<ESLPopupActionParams>()
+  public override defaultParams: ESLPopupActionParams;
 
   @attr({parser: parseBoolean, serializer: toBooleanAttribute, defaultValue: true})
   public override closeOnEsc: boolean;
@@ -188,11 +193,11 @@ export class ESLPopup extends ESLToggleable {
    * Inner state and 'open' attribute are not affected and updated before `onShow` execution.
    * Adds CSS classes, update a11y and fire esl:refresh event by default.
    */
-  protected override onShow(params: PopupActionParams): void {
-    if (this.open) {
-      this.beforeOnHide();
-      super.onHide(params);
-      this.afterOnHide();
+  protected override onShow(params: ESLPopupActionParams): void {
+    const wasOpened = this.open;
+    if (wasOpened) {
+      this.beforeOnHide(params);
+      this.afterOnHide(params);
     }
 
     super.onShow(params);
@@ -216,7 +221,12 @@ export class ESLPopup extends ESLToggleable {
 
     this.style.visibility = 'hidden'; // eliminates the blinking of the popup at the previous position
 
-    afterNextRender(() => this.afterOnShow()); // running as a separate task solves the problem with incorrect positioning on the first showing
+    // running as a separate task solves the problem with incorrect positioning on the first showing
+    if (wasOpened) this.afterOnShow(params);
+    else afterNextRender(() => this.afterOnShow(params));
+
+    // Autofocus logic
+    afterNextRender(() => params.autofocus && this.focus({preventScroll: true}));
   }
 
   /**
@@ -224,16 +234,17 @@ export class ESLPopup extends ESLToggleable {
    * Inner state and 'open' attribute are not affected and updated before `onShow` execution.
    * Removes CSS classes and updates a11y by default.
    */
-  protected override onHide(params: PopupActionParams): void {
-    this.beforeOnHide();
+  protected override onHide(params: ESLPopupActionParams): void {
+    this.beforeOnHide(params);
     super.onHide(params);
-    this.afterOnHide();
+    this.afterOnHide(params);
+    params.autofocus && this.activator?.focus({preventScroll: true});
   }
 
   /**
    * Actions to execute after showing of popup.
    */
-  protected afterOnShow(): void {
+  protected afterOnShow(params: ESLPopupActionParams): void {
     this._updatePosition();
 
     this.style.visibility = 'visible';
@@ -251,12 +262,12 @@ export class ESLPopup extends ESLToggleable {
   /**
    * Actions to execute before hiding of popup.
    */
-  protected beforeOnHide(): void {}
+  protected beforeOnHide(params: ESLPopupActionParams): void {}
 
   /**
    * Actions to execute after hiding of popup.
    */
-  protected afterOnHide(): void {
+  protected afterOnHide(params: ESLPopupActionParams): void {
     this._stopUpdateLoop();
 
     this.$$attr('style', '');
diff --git a/src/modules/esl-share/core/esl-share-popup.ts b/src/modules/esl-share/core/esl-share-popup.ts
index 0097dfe76..8bb885f82 100644
--- a/src/modules/esl-share/core/esl-share-popup.ts
+++ b/src/modules/esl-share/core/esl-share-popup.ts
@@ -4,7 +4,7 @@ import {bind, listen, memoize, prop} from '../../esl-utils/decorators';
 import {ESLShareButton} from './esl-share-button';
 import {ESLShareConfig} from './esl-share-config';
 
-import type {TooltipActionParams} from '../../esl-tooltip/core/esl-tooltip';
+import type {ESLTooltipActionParams} from '../../esl-tooltip/core/esl-tooltip';
 import type {ESLShareButtonConfig} from './esl-share-config';
 
 export type {ESLSharePopupTagShape} from './esl-share-popup.shape';
@@ -13,7 +13,7 @@ function stringifyButtonsList(btns: ESLShareButtonConfig[]): string {
   return btns.map((btn) => btn.name).join(',');
 }
 
-export interface ESLSharePopupActionParams extends TooltipActionParams {
+export interface ESLSharePopupActionParams extends ESLTooltipActionParams {
   /** list of social networks or groups of them to display */
   list?: string;
 }
@@ -58,7 +58,7 @@ export class ESLSharePopup extends ESLTooltip {
   /** Hashstring with a list of buttons already rendered in the popup */
   protected _list: string = '';
 
-  public override onShow(params: TooltipActionParams): void {
+  public override onShow(params: ESLTooltipActionParams): void {
     if (params.list) {
       const buttonsList = ESLShareConfig.instance.get(params.list);
       this.appendButtonsFromList(buttonsList);
diff --git a/src/modules/esl-toggleable/core/esl-toggleable.ts b/src/modules/esl-toggleable/core/esl-toggleable.ts
index da293ad47..2fffc9d32 100644
--- a/src/modules/esl-toggleable/core/esl-toggleable.ts
+++ b/src/modules/esl-toggleable/core/esl-toggleable.ts
@@ -14,6 +14,8 @@ import type {DelegatedEvent} from '../../esl-event-listener/core/types';
 
 /** Default Toggleable action params type definition */
 export interface ESLToggleableActionParams {
+  /** Action to execute */
+  readonly action?: 'show' | 'hide';
   /** Initiator string identifier */
   initiator?: string;
   /** Delay timeout for both show and hide actions */
@@ -220,18 +222,18 @@ export class ESLToggleable extends ESLBaseElement {
 
   /** Actual show task to execute by toggleable task manger ({@link DelayedTask} out of the box) */
   protected showTask(params: ESLToggleableActionParams): void {
+    Object.defineProperty(params, 'action', {value: 'show', writable: false});
     if (!this.shouldShow(params)) return;
     if (!params.silent && !this.$$fire(this.BEFORE_SHOW_EVENT, {detail: {params}})) return;
     this.activator = params.activator;
-    this.open = true;
     this.onShow(params);
     if (!params.silent) this.$$fire(this.SHOW_EVENT, {detail: {params}, cancelable: false});
   }
   /** Actual hide task to execute by toggleable task manger ({@link DelayedTask} out of the box) */
   protected hideTask(params: ESLToggleableActionParams): void {
+    Object.defineProperty(params, 'action', {value: 'hide', writable: false});
     if (!this.shouldHide(params)) return;
     if (!params.silent && !this.$$fire(this.BEFORE_HIDE_EVENT, {detail: {params}})) return;
-    this.open = false;
     this.onHide(params);
     this.bindOutsideEventTracking(false);
     if (!params.silent) this.$$fire(this.HIDE_EVENT, {detail: {params}, cancelable: false});
@@ -251,6 +253,7 @@ export class ESLToggleable extends ESLBaseElement {
    * Adds CSS classes, update a11y and fire {@link ESLToggleable.REFRESH_EVENT} event by default.
    */
   protected onShow(params: ESLToggleableActionParams): void {
+    this.open = true;
     CSSClassUtils.add(this, this.activeClass);
     CSSClassUtils.add(document.body, this.bodyClass, this);
     if (this.containerActiveClass) {
@@ -276,6 +279,7 @@ export class ESLToggleable extends ESLBaseElement {
    * Removes CSS classes and update a11y by default.
    */
   protected onHide(params: ESLToggleableActionParams): void {
+    this.open = false;
     CSSClassUtils.remove(this, this.activeClass);
     CSSClassUtils.remove(document.body, this.bodyClass, this);
     if (this.containerActiveClass) {
diff --git a/src/modules/esl-tooltip/core/esl-tooltip.ts b/src/modules/esl-tooltip/core/esl-tooltip.ts
index 16541050a..fd19f44a9 100644
--- a/src/modules/esl-tooltip/core/esl-tooltip.ts
+++ b/src/modules/esl-tooltip/core/esl-tooltip.ts
@@ -4,10 +4,10 @@ import {memoize, attr, boolAttr, listen, prop} from '../../esl-utils/decorators'
 import {TAB} from '../../esl-utils/dom/keys';
 import {getKeyboardFocusableElements, handleFocusChain} from '../../esl-utils/dom/focus';
 
-import type {PopupActionParams} from '../../esl-popup/core';
+import type {ESLPopupActionParams} from '../../esl-popup/core';
 import type {PositionType} from '../../esl-popup/core/esl-popup-position';
 
-export interface TooltipActionParams extends PopupActionParams {
+export interface ESLTooltipActionParams extends ESLPopupActionParams {
   /** text to be shown */
   text?: string;
   /** html content to be shown */
@@ -20,10 +20,19 @@ export interface TooltipActionParams extends PopupActionParams {
   disableArrow?: boolean;
 }
 
+/** @deprecated alias, use {@link ESLTooltipActionParams} instead. Will be removed in v5.0.0. */
+export type TooltipActionParams = ESLTooltipActionParams;
+
 @ExportNs('Tooltip')
 export class ESLTooltip extends ESLPopup {
   static override is = 'esl-tooltip';
 
+  /** Default params to pass into the tooltip on show/hide actions */
+  public static override DEFAULT_PARAMS: ESLTooltipActionParams = {
+    ...ESLPopup.DEFAULT_PARAMS,
+    autofocus: true
+  };
+
   @prop(false) public hasFocusLoop: boolean;
 
   /**
@@ -63,12 +72,12 @@ export class ESLTooltip extends ESLPopup {
   }
 
   /** Changes the element state to active */
-  public static show(params: TooltipActionParams = {}): void {
+  public static show(params: ESLTooltipActionParams = {}): void {
     this.sharedInstance.show(params);
   }
 
   /** Changes the element state to inactive */
-  public static hide(params: TooltipActionParams = {}): void {
+  public static hide(params: ESLTooltipActionParams = {}): void {
     this.sharedInstance.hide(params);
   }
 
@@ -83,7 +92,7 @@ export class ESLTooltip extends ESLPopup {
   protected override setInitialState(): void {}
 
   /** Actions to execute on show Tooltip. */
-  public override onShow(params: TooltipActionParams): void {
+  public override onShow(params: ESLTooltipActionParams): void {
     if (params.disableArrow) {
       this.disableArrow = params.disableArrow;
     }
@@ -100,27 +109,11 @@ export class ESLTooltip extends ESLPopup {
   }
 
   /** Actions to execute on Tooltip hiding. */
-  public override onHide(params: TooltipActionParams): void {
+  public override onHide(params: ESLTooltipActionParams): void {
     super.onHide(params);
     this.parentNode === document.body && document.body.removeChild(this);
   }
 
-  /**
-   * Actions to execute after showing of popup.
-   */
-  protected override afterOnShow(): void {
-    super.afterOnShow();
-    this.focus({preventScroll: true});
-  }
-
-  /**
-   * Actions to execute before hiding of popup.
-   */
-  protected override beforeOnHide(): void {
-    super.beforeOnHide();
-    this.activator?.focus({preventScroll: true});
-  }
-
   @listen({inherit: true})
   protected override _onKeyboardEvent(e: KeyboardEvent): void {
     super._onKeyboardEvent(e);


Test patch:
diff --git a/src/modules/esl-alert/test/alert.request.test.ts b/src/modules/esl-alert/test/alert.request.test.ts
index 73f84c4f1..04719575e 100644
--- a/src/modules/esl-alert/test/alert.request.test.ts
+++ b/src/modules/esl-alert/test/alert.request.test.ts
@@ -1,6 +1,6 @@
 import {ESLEventUtils} from '../../esl-event-listener/core/api';
 import {ESLAlert} from '../core/esl-alert';
-import type {AlertActionParams} from '../core/esl-alert';
+import type {ESLAlertActionParams} from '../core/esl-alert';
 
 describe('ESLAlert: general', () => {
   beforeAll(() => ESLAlert.register());
@@ -32,7 +32,7 @@ describe('ESLAlert: events', () => {
   });
 
   test('Show/Hide events ', () => {
-    ESLEventUtils.dispatch($el, ESLAlert.prototype.SHOW_REQUEST_EVENT);
+    ESLEventUtils.dispatch($el, ESLAlert.prototype.SHOW_REQUEST_EVENT, {detail: {text: 'esl-alert-text'}});
     jest.advanceTimersByTime(1);
     expect($el.open).toBe(true);
     ESLEventUtils.dispatch($el, ESLAlert.prototype.HIDE_REQUEST_EVENT);
@@ -41,7 +41,7 @@ describe('ESLAlert: events', () => {
   });
 
   test('Auto hide by hideTime & hideDelay ', () => {
-    const params: CustomEventInit<AlertActionParams> = {detail: {hideTime: 100, hideDelay: 100}};
+    const params: CustomEventInit<ESLAlertActionParams> = {detail: {hideTime: 100, hideDelay: 100, text: 'esl-alert-text'}};
     ESLEventUtils.dispatch($el, ESLAlert.prototype.SHOW_REQUEST_EVENT, params);
     jest.advanceTimersByTime(1);
     expect($el.open).toBe(true);
@@ -50,19 +50,19 @@ describe('ESLAlert: events', () => {
   });
 
   test('Add text params', () => {
-    const params: CustomEventInit<AlertActionParams> = {detail: {text: 'esl-alert-text'}};
+    const params: CustomEventInit<ESLAlertActionParams> = {detail: {text: 'esl-alert-text'}};
     ESLEventUtils.dispatch($el, ESLAlert.prototype.SHOW_REQUEST_EVENT, params);
     expect('esl-alert-text').toEqual($el.textContent);
   });
 
   test('Add HTML params', () => {
-    const params: CustomEventInit<AlertActionParams> = {detail: {html: '<h2>esl-alert-html</h2>'}};
+    const params: CustomEventInit<ESLAlertActionParams> = {detail: {html: '<h2>esl-alert-html</h2>'}};
     ESLEventUtils.dispatch($el, ESLAlert.prototype.SHOW_REQUEST_EVENT, params);
     expect('<h2>esl-alert-html</h2>').toEqual($el.children[0].innerHTML);
   });
 
   test('Add class params', () => {
-    const params: CustomEventInit<AlertActionParams> = {detail: {cls: 'esl-alert-cls',  text: 'esl-alert-text'}};
+    const params: CustomEventInit<ESLAlertActionParams> = {detail: {cls: 'esl-alert-cls',  text: 'esl-alert-text'}};
     ESLEventUtils.dispatch($el, ESLAlert.prototype.SHOW_REQUEST_EVENT, params);
     expect($el.className).toContain('esl-alert-cls');
   });