From 3b8e15389325046d9b44d35c82abc69ba19b2194 Mon Sep 17 00:00:00 2001
From: Pavel Klibani <pavel@techloop.io>
Date: Fri, 2 Feb 2024 15:43:42 +0100
Subject: [PATCH 1/5] Fix(web-react): Add forwardRef to Icon components

- Icon component now accept forwarded ref and can be used as TooltipTrigger
---
 .../web-react/src/components/Icon/Icon.tsx     | 10 ++++++++--
 .../src/components/Icon/demo/Icon.tsx          | 18 ------------------
 .../src/components/Icon/demo/index.tsx         |  2 +-
 3 files changed, 9 insertions(+), 21 deletions(-)
 delete mode 100644 packages/web-react/src/components/Icon/demo/Icon.tsx

diff --git a/packages/web-react/src/components/Icon/Icon.tsx b/packages/web-react/src/components/Icon/Icon.tsx
index 0b216cc941..cd5a7a2154 100644
--- a/packages/web-react/src/components/Icon/Icon.tsx
+++ b/packages/web-react/src/components/Icon/Icon.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { ForwardedRef, forwardRef } from 'react';
 import { warning } from '../../common/utilities';
 import { useIcon, useStyleProps } from '../../hooks';
 import { IconProps } from '../../types';
@@ -10,7 +10,9 @@ const defaultProps = {
   boxSize: 24,
 };
 
-export const Icon = (props: IconProps) => {
+/* We need an exception for components exported with forwardRef */
+/* eslint no-underscore-dangle: ['error', { allow: ['_Icon'] }] */
+export const _Icon = (props: IconProps, ref: ForwardedRef<SVGSVGElement>) => {
   const { boxSize, name, title, ariaHidden, ...restProps } = props;
   let icon = useIcon(name);
   const { styleProps, props: otherProps } = useStyleProps(restProps);
@@ -41,6 +43,7 @@ export const Icon = (props: IconProps) => {
         {...otherProps}
         {...styleProps}
         className={styleProps.className}
+        ref={ref}
       >
         {/* @ts-expect-error TS2349: This expression is not callable. Type 'never' has no call signatures. */}
         {htmlParser(icon)}
@@ -64,11 +67,14 @@ export const Icon = (props: IconProps) => {
         {...otherProps}
         {...styleProps}
         className={styleProps.className}
+        ref={ref}
       />
     </NoSsr>
   );
 };
 
+export const Icon = forwardRef<SVGSVGElement, IconProps>(_Icon);
+
 Icon.defaultProps = defaultProps;
 
 export default Icon;
diff --git a/packages/web-react/src/components/Icon/demo/Icon.tsx b/packages/web-react/src/components/Icon/demo/Icon.tsx
deleted file mode 100644
index 15529ee778..0000000000
--- a/packages/web-react/src/components/Icon/demo/Icon.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-// Because there is no `dist` directory during the CI run
-/* eslint-disable import/no-extraneous-dependencies, import/extensions, import/no-unresolved */
-import React from 'react';
-import { ComponentStory } from '@storybook/react';
-// eslint-disable-next-line @typescript-eslint/ban-ts-comment
-// @ts-ignore: No declaration file
-import icons from '@lmc-eu/spirit-icons/dist/icons';
-import { IconsProvider } from '../../../context';
-import Icon from '../Icon';
-import { IconProps } from '../../../types';
-
-const Story: ComponentStory<typeof Icon> = (args: IconProps) => (
-  <IconsProvider value={icons}>
-    <Icon {...args} />
-  </IconsProvider>
-);
-
-export default Story;
diff --git a/packages/web-react/src/components/Icon/demo/index.tsx b/packages/web-react/src/components/Icon/demo/index.tsx
index 181c12f287..f42026ccc9 100644
--- a/packages/web-react/src/components/Icon/demo/index.tsx
+++ b/packages/web-react/src/components/Icon/demo/index.tsx
@@ -7,7 +7,7 @@ import ReactDOM from 'react-dom/client';
 import icons from '@lmc-eu/spirit-icons/dist/icons';
 import { IconsProvider } from '../../../context';
 import DocsSection from '../../../../docs/DocsSections';
-import Icon from './Icon';
+import Icon from '../Icon';
 
 ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
   <React.StrictMode>

From f1165369b1c696e43ac1b66b6d94553e7b9d7535 Mon Sep 17 00:00:00 2001
From: Pavel Klibani <pavel@techloop.io>
Date: Fri, 2 Feb 2024 15:45:07 +0100
Subject: [PATCH 2/5] Fix(web-react): TooltipTrigger props update

- elementType can be now self closing (fe. Icon)
- accepting props from elementType components
---
 .../web-react/src/components/Tooltip/TooltipTrigger.tsx    | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/packages/web-react/src/components/Tooltip/TooltipTrigger.tsx b/packages/web-react/src/components/Tooltip/TooltipTrigger.tsx
index ceea9507b7..40806fe2cd 100644
--- a/packages/web-react/src/components/Tooltip/TooltipTrigger.tsx
+++ b/packages/web-react/src/components/Tooltip/TooltipTrigger.tsx
@@ -1,11 +1,11 @@
 import React, { ElementType, ReactNode } from 'react';
 import { useStyleProps } from '../../hooks';
-import { StyleProps } from '../../types';
+import { StyleProps, TransferProps } from '../../types';
 import { useTooltipContext } from './TooltipContext';
 
-interface TooltipTriggerProps extends StyleProps {
+interface TooltipTriggerProps extends StyleProps, TransferProps {
   elementType?: ElementType | string;
-  children: string | ReactNode | ((props: { isOpen: boolean }) => React.ReactNode);
+  children?: string | ReactNode | ((props: { isOpen: boolean }) => React.ReactNode);
 }
 
 const TooltipTrigger = (props: TooltipTriggerProps) => {
@@ -25,6 +25,7 @@ const TooltipTrigger = (props: TooltipTriggerProps) => {
 
 TooltipTrigger.defaultProps = {
   elementType: 'button',
+  children: null,
 };
 
 export default TooltipTrigger;

From 84b227df8ee733ce065035496f4996701ecf29ad Mon Sep 17 00:00:00 2001
From: Pavel Klibani <pavel@techloop.io>
Date: Fri, 2 Feb 2024 15:47:24 +0100
Subject: [PATCH 3/5] Docs(web-react): Add demo with Tooltip on Icon cmp

- New demo with tooltip on Icon component
---
 .../TooltipModern/demo/TooltipIcon.tsx        | 35 +++++++++++++++++++
 .../components/TooltipModern/demo/index.tsx   |  4 +++
 2 files changed, 39 insertions(+)
 create mode 100644 packages/web-react/src/components/TooltipModern/demo/TooltipIcon.tsx

diff --git a/packages/web-react/src/components/TooltipModern/demo/TooltipIcon.tsx b/packages/web-react/src/components/TooltipModern/demo/TooltipIcon.tsx
new file mode 100644
index 0000000000..8c95877679
--- /dev/null
+++ b/packages/web-react/src/components/TooltipModern/demo/TooltipIcon.tsx
@@ -0,0 +1,35 @@
+import React, { useState } from 'react';
+import { Icon } from '../../Icon';
+import { TooltipModern, TooltipPopover, TooltipTrigger } from '../../Tooltip';
+
+const TooltipIcon = () => {
+  const [openIcon, setOpenIcon] = useState(false);
+
+  return (
+    <div className="spirit-feature-tooltip-enable-data-placement">
+      <p>
+        Click on the icon on the right to view the tooltip{' '}
+        <TooltipModern
+          id="tooltip-example-with-icon"
+          isOpen={openIcon}
+          onToggle={setOpenIcon}
+          placement="right"
+          flipFallbackPlacements={['top-start', 'bottom-start', 'left']}
+          UNSAFE_className="d-inline-block"
+          isDismissible
+        >
+          <TooltipTrigger
+            elementType={Icon}
+            name="info"
+            boxSize={16}
+            UNSAFE_className="d-inline-block"
+            marginBottom="space-200"
+          />
+          <TooltipPopover>Close me</TooltipPopover>
+        </TooltipModern>
+      </p>
+    </div>
+  );
+};
+
+export default TooltipIcon;
diff --git a/packages/web-react/src/components/TooltipModern/demo/index.tsx b/packages/web-react/src/components/TooltipModern/demo/index.tsx
index 5a8bbd4cf8..f9711f85a6 100644
--- a/packages/web-react/src/components/TooltipModern/demo/index.tsx
+++ b/packages/web-react/src/components/TooltipModern/demo/index.tsx
@@ -13,6 +13,7 @@ import TooltipDismissibleViaJS from './TooltipDismissibleViaJS';
 import TooltipOnHover from './TooltipOnHover';
 import TooltipPlacements from './TooltipPlacements';
 import TooltipWithFloatingUI from './TooltipWithFloatingUI';
+import TooltipIcon from './TooltipIcon';
 
 ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
   <React.StrictMode>
@@ -32,6 +33,9 @@ ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
       <DocsSection title="Dismissible Tooltip via JS API and Floating UI">
         <TooltipDismissibleViaJS />
       </DocsSection>
+      <DocsSection title="Tooltip on Icon Component">
+        <TooltipIcon />
+      </DocsSection>
       <DocsSection title="Advanced Floating Functionality" stackAlignment="stretch">
         <TooltipWithFloatingUI />
       </DocsSection>

From a3e338c3b33911bcc250caa538cfe449c7f39ffa Mon Sep 17 00:00:00 2001
From: Pavel Klibani <pavel@techloop.io>
Date: Fri, 2 Feb 2024 15:48:35 +0100
Subject: [PATCH 4/5] Docs(web-twig): Add demo with Tooltip on Icon cmp

- New demo with tooltip on Icon component
---
 .../components/Tooltip/Tooltip.stories.twig   |  4 ++++
 .../Tooltip/stories/TooltipIcon.twig          | 24 +++++++++++++++++++
 2 files changed, 28 insertions(+)
 create mode 100644 packages/web-twig/src/Resources/components/Tooltip/stories/TooltipIcon.twig

diff --git a/packages/web-twig/src/Resources/components/Tooltip/Tooltip.stories.twig b/packages/web-twig/src/Resources/components/Tooltip/Tooltip.stories.twig
index b703a709ea..7962597faf 100644
--- a/packages/web-twig/src/Resources/components/Tooltip/Tooltip.stories.twig
+++ b/packages/web-twig/src/Resources/components/Tooltip/Tooltip.stories.twig
@@ -30,6 +30,10 @@
         {% include '@components/Tooltip/stories/TooltipDismissibleViaJS.twig' %}
     </DocsSection>
 
+    <DocsSection title="Tooltip on Icon Component">
+        {% include '@components/Tooltip/stories/TooltipIcon.twig' %}
+    </DocsSection>
+
     <DocsSection title="Advanced Floating Functionality">
         {% include '@components/Tooltip/stories/TooltipFloatingUI.twig' %}
     </DocsSection>
diff --git a/packages/web-twig/src/Resources/components/Tooltip/stories/TooltipIcon.twig b/packages/web-twig/src/Resources/components/Tooltip/stories/TooltipIcon.twig
new file mode 100644
index 0000000000..88ba2c215e
--- /dev/null
+++ b/packages/web-twig/src/Resources/components/Tooltip/stories/TooltipIcon.twig
@@ -0,0 +1,24 @@
+<span>
+  Click on the icon on the right to view the tooltip
+  <TooltipWrapper UNSAFE_className="spirit-feature-tooltip-enable-data-placement d-inline-block">
+        <Icon
+          id="button-example-with-icon"
+          name="info"
+          boxSize="16"
+          marginBottom="space-200"
+          aria-describedby="tooltip-example-with-icon"
+          data-spirit-toggle="tooltip"
+          data-spirit-target="#tooltip-example-with-icon"
+      />
+      <TooltipPopover
+          id="tooltip-example-with-icon"
+          UNSAFE_className="is-hidden"
+          enableControlledPlacement
+          placement="right"
+          flipFallbackPlacements="top-start, top-bottom, left"
+          isDismissible
+      >
+          Close me
+      </TooltipPopov>
+  </TooltipWrapper>
+</span>
\ No newline at end of file

From 48da1b2c200d257778aa245401963a4d7f70e80e Mon Sep 17 00:00:00 2001
From: Pavel Klibani <pavel@techloop.io>
Date: Fri, 2 Feb 2024 15:49:08 +0100
Subject: [PATCH 5/5] Docs(web): Add demo with Tooltip on Icon element

- New demo with tooltip on Icon element
---
 .../src/scss/components/Tooltip/index.html    | 47 +++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/packages/web/src/scss/components/Tooltip/index.html b/packages/web/src/scss/components/Tooltip/index.html
index 3b8f4c7802..a6f4064988 100644
--- a/packages/web/src/scss/components/Tooltip/index.html
+++ b/packages/web/src/scss/components/Tooltip/index.html
@@ -444,6 +444,53 @@ <h2 class="docs-Heading">Dismissible Tooltip via JS API and Floating UI</h2>
 
   </section>
 
+  <section class="docs-Section spirit-feature-tooltip-enable-data-placement">
+
+    <h2 class="docs-Heading">Tooltip on Icon Component</h2>
+
+    <div class="docs-Stack docs-Stack--start">
+
+      <span>
+        Click on the icon on the right to view the tooltip
+        <div class="TooltipWrapper d-inline-block" data-spirit-element="tooltip-wrapper">
+          <svg 
+            width="16" 
+            height="16" 
+            data-spirit-toggle="tooltip"
+            data-spirit-target="#tooltip-example-with-icon">
+            <use xlink:href="/icons/svg/sprite.svg#info"/>
+          </svg>
+          <div
+            id="tooltip-example-with-icon"
+            class="Tooltip Tooltip--dismissible is-hidden"
+            data-spirit-placement="right"
+            data-spirit-flip="true"
+            data-spirit-flip-cross-axis="true"
+            data-spirit-flip-fallback-placements="top-start, bottom-start, left"
+            data-spirit-placement-controlled
+          >
+            Close me
+            <button
+              id="tooltip-example-with-icon-button"
+              type="button"
+              class="Tooltip__close"
+              aria-controls="tooltip-example-with-icon"
+              aria-expanded="true"
+            >
+              <svg width="24" height="24" aria-hidden="true">
+                <use xlink:href="/icons/svg/sprite.svg#close" />
+              </svg>
+              <span class="accessibility-hidden">Close</span>
+            </button>
+            <span class="Tooltip__arrow" data-spirit-element="arrow"></span>
+          </div>
+        </div>
+      </span>
+
+    </div>
+
+  </section>
+
   <section class="docs-Section spirit-feature-tooltip-enable-data-placement">
 
     <script type="module" src="floating-ui-example.mjs"></script>