Tooltip shouldWrapChildren 的運作原理

Tooltip 預設行為是用 React.cloneElement 把 hover 事件直接注入到 child element:

// Chakra Tooltip 內部大致做法
const child = React.Children.only(children);
return React.cloneElement(child, { onMouseEnter, onMouseLeave, ref });

沒有 shouldWrapChildren 的情況

// 結構
<Tooltip label="...">
  <ProjectLockGuard>   {/* ← Tooltip 把 onMouseEnter/onMouseLeave 加到這裡 */}
    <Button>...</Button>
  </ProjectLockGuard>
</Tooltip>

ProjectLockGuard 的 props 介面只有 children

interface ProjectLockGuardProps {
  children: React.ReactElement;
}
 
const ProjectLockGuard: React.FC<ProjectLockGuardProps> = ({ children }) => {
  // onMouseEnter, onMouseLeave 被丟掉了,沒有 forward 給 Button
  return children;
};

Tooltip 注入的 onMouseEnter / onMouseLeaveProjectLockGuard 吃掉,不會傳到 Button → hover 無反應,tooltip 不會出現。

加了 shouldWrapChildren 的情況

// 結構
<Tooltip label="..." shouldWrapChildren>
  <span>                               {/* ← Tooltip 自動產生的 <span>,hover 事件掛在這 */}
    <ProjectLockGuard>
      <Button>...</Button>
    </ProjectLockGuard>
  </span>
</Tooltip>

Tooltip 改成在外面包一個 <span>,hover 事件掛在 <span> 上,不需要對 child 做 cloneElement,所以不管裡面是什麼 component 都能正常觸發。

前後對比

Before — tooltip 不會出現:

<Tooltip label={t('tooltipBulkSetAllocation')}>
  <ProjectLockGuard>        {/* ← cloneElement 加在這,props 被丟掉 */}
    <Button>...</Button>
  </ProjectLockGuard>
</Tooltip>

After — tooltip 正常運作:

<Tooltip
  label={t('tooltipBulkSetAllocation')}
  shouldWrapChildren           // ← 多包一層 <span> 接收 hover 事件
  isDisabled={isLocked}        // ← locked 時不顯示此 tooltip,改由 ProjectLockGuard 顯示 lock tooltip
>
  <ProjectLockGuard>
    <Button>...</Button>
  </ProjectLockGuard>
</Tooltip>

狀態對照表

狀態外層 TooltipProjectLockGuard結果
Not lockedisDisabled=false → 顯示業務 tooltip直接 return Buttonhover 顯示 tooltipBulkSetAllocation
LockedisDisabled=true → 不顯示包自己的 lock Tooltip + disabled Buttonhover 顯示 lock 提示,按鈕不可點擊