Repository: wandb/weave
Pull request #4589: chore(weave): icons and icon safety
Author: login: onx2
Created: 2025-05-27T15:06:54Z | Merged: 2025-05-28T20:41:58Z

## Description

Fixes WB-25017

Adds type safety to icons
Use internal icon component instead of wandb/ui



Diff:
diff --git a/weave-js/src/assets/icons/icon-overlay-2-column.svg b/weave-js/src/assets/icons/icon-overlay-2-column.svg
new file mode 100644
index 000000000000..979e1717ed8a
--- /dev/null
+++ b/weave-js/src/assets/icons/icon-overlay-2-column.svg
@@ -0,0 +1 @@
+<svg width="24" height="24" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M4 2.667h9.714c.736 0 1.333.597 1.333 1.333v4.783c0 .736-.597 1.333-1.333 1.333H4a1.333 1.333 0 0 1-1.333-1.333V4c0-.736.597-1.333 1.333-1.333zm0 9.77h9.714c.736 0 1.333.597 1.333 1.333v4.783c0 .736-.597 1.333-1.333 1.333H4a1.333 1.333 0 0 1-1.333-1.333V13.77c0-.736.597-1.333 1.333-1.333zm0 9.771h9.714c.736 0 1.333.597 1.333 1.333V28c0 .736-.597 1.333-1.333 1.333H4A1.333 1.333 0 0 1 2.667 28v-4.459c0-.736.597-1.333 1.333-1.333zM18.287 2.667h9.714c.736 0 1.333.597 1.333 1.333v4.783c0 .736-.597 1.333-1.333 1.333h-9.714a1.333 1.333 0 0 1-1.333-1.333V4c0-.736.597-1.333 1.333-1.333zm0 9.77h9.714c.736 0 1.333.597 1.333 1.333v4.783c0 .736-.597 1.333-1.333 1.333h-9.714a1.333 1.333 0 0 1-1.333-1.333V13.77c0-.736.597-1.333 1.333-1.333zm0 9.771h9.714c.736 0 1.333.597 1.333 1.333V28c0 .736-.597 1.333-1.333 1.333h-9.714A1.333 1.333 0 0 1 16.954 28v-4.459c0-.736.597-1.333 1.333-1.333z" /></svg>
\ No newline at end of file
diff --git a/weave-js/src/assets/icons/icon-overlay-3-column.svg b/weave-js/src/assets/icons/icon-overlay-3-column.svg
new file mode 100644
index 000000000000..a2979ae72de4
--- /dev/null
+++ b/weave-js/src/assets/icons/icon-overlay-3-column.svg
@@ -0,0 +1 @@
+<svg width="24" height="24" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M4 2.667h4.952c.736 0 1.333.597 1.333 1.333v4.606c0 .736-.597 1.333-1.333 1.333H4a1.333 1.333 0 0 1-1.333-1.333V4c0-.736.597-1.333 1.333-1.333zm0 9.697h4.952c.736 0 1.333.597 1.333 1.333v4.606c0 .736-.597 1.333-1.333 1.333H4a1.333 1.333 0 0 1-1.333-1.333v-4.606c0-.736.597-1.333 1.333-1.333zm0 9.697h4.952c.736 0 1.333.597 1.333 1.333V28c0 .736-.597 1.333-1.333 1.333H4A1.333 1.333 0 0 1 2.667 28v-4.606c0-.736.597-1.333 1.333-1.333zm9.524-19.394h4.952c.736 0 1.333.597 1.333 1.333v4.606c0 .736-.597 1.333-1.333 1.333h-4.952a1.333 1.333 0 0 1-1.333-1.333V4c0-.736.597-1.333 1.333-1.333zm0 9.697h4.952c.736 0 1.333.597 1.333 1.333v4.606c0 .736-.597 1.333-1.333 1.333h-4.952a1.333 1.333 0 0 1-1.333-1.333v-4.606c0-.736.597-1.333 1.333-1.333zm0 9.697h4.952c.736 0 1.333.597 1.333 1.333V28c0 .736-.597 1.333-1.333 1.333h-4.952A1.333 1.333 0 0 1 12.191 28v-4.606c0-.736.597-1.333 1.333-1.333zm9.524-19.394H28c.736 0 1.333.597 1.333 1.333v4.606c0 .736-.597 1.333-1.333 1.333h-4.952a1.333 1.333 0 0 1-1.333-1.333V4c0-.736.597-1.333 1.333-1.333zm0 9.697H28c.736 0 1.333.597 1.333 1.333v4.606c0 .736-.597 1.333-1.333 1.333h-4.952a1.333 1.333 0 0 1-1.333-1.333v-4.606c0-.736.597-1.333 1.333-1.333zm0 9.697H28c.736 0 1.333.597 1.333 1.333V28c0 .736-.597 1.333-1.333 1.333h-4.952A1.333 1.333 0 0 1 21.715 28v-4.606c0-.736.597-1.333 1.333-1.333z" /></svg>
\ No newline at end of file
diff --git a/weave-js/src/assets/icons/icon-overlay-stack.svg b/weave-js/src/assets/icons/icon-overlay-stack.svg
new file mode 100644
index 000000000000..5c719f98aba3
--- /dev/null
+++ b/weave-js/src/assets/icons/icon-overlay-stack.svg
@@ -0,0 +1 @@
+<svg width="24" height="24" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M2.667 4.253c0-.876.71-1.586 1.586-1.586h14.351c.876 0 1.586.71 1.586 1.586v1.459H8.825a3.113 3.113 0 0 0-3.113 3.113V20.19H4.253c-.876 0-1.586-.71-1.586-1.586V4.253zm6.158 2.986h14.351c.876 0 1.586.71 1.586 1.586v1.459H13.397a3.036 3.036 0 0 0-.922.139 3.117 3.117 0 0 0-2.19 2.974v11.365H8.826c-.876 0-1.586-.71-1.586-1.586V8.825c0-.876.71-1.586 1.586-1.586zm2.986 17.522v2.986c0 .876.71 1.586 1.586 1.586h14.351c.876 0 1.586-.71 1.586-1.586V13.396c0-.876-.71-1.586-1.586-1.586H13.397a1.587 1.587 0 0 0-1.586 1.586v11.365z" /></svg>
\ No newline at end of file
diff --git a/weave-js/src/assets/icons/icon-panel-images.svg b/weave-js/src/assets/icons/icon-panel-images.svg
new file mode 100644
index 000000000000..2763b88696b8
--- /dev/null
+++ b/weave-js/src/assets/icons/icon-panel-images.svg
@@ -0,0 +1 @@
+<svg width="24" height="24" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M28 6v20H4V6h24zM4 4a2 2 0 0 0-2 2v20a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2H4zm7.932 6.339a1 1 0 0 1 1.217.041l6.607 5.431a1 1 0 1 1-1.271 1.545l-6.014-4.944-8.916 6.38a1 1 0 0 1-1.164-1.626l9.54-6.827zm10.479 3.101a.999.999 0 0 1 1.229.034l6.022 4.966a1 1 0 1 1-1.273 1.543l-5.42-4.47-8.336 6.14a.999.999 0 1 1-1.186-1.61l8.964-6.603z" /></svg>
diff --git a/weave-js/src/common/components/FileBrowser.styles.ts b/weave-js/src/common/components/FileBrowser.styles.ts
index 8d2cc0587606..4f412f7debe1 100644
--- a/weave-js/src/common/components/FileBrowser.styles.ts
+++ b/weave-js/src/common/components/FileBrowser.styles.ts
@@ -1,7 +1,7 @@
-import {WBIcon} from '@wandb/ui';
 import {Table} from 'semantic-ui-react';
 import styled from 'styled-components';
 
+import {Icon} from '../../components/Icon';
 import * as globals from '../css/globals.styles';
 
 export const FileTableBody = styled(Table.Body)`
@@ -61,7 +61,7 @@ export const SearchInputContainer = styled.span`
   }
 `;
 
-export const SearchInputIcon = styled(WBIcon)`
+export const SearchInputIcon: typeof Icon = styled(Icon)`
   color: ${globals.gray500};
   width: 24px;
   font-size: 28px;
diff --git a/weave-js/src/common/components/Vega3/CustomPanelRenderer.styles.ts b/weave-js/src/common/components/Vega3/CustomPanelRenderer.styles.ts
index 57b20050d09f..ca6d7d58fa7c 100644
--- a/weave-js/src/common/components/Vega3/CustomPanelRenderer.styles.ts
+++ b/weave-js/src/common/components/Vega3/CustomPanelRenderer.styles.ts
@@ -1,6 +1,6 @@
-import {WBIcon} from '@wandb/ui';
 import styled, {css} from 'styled-components';
 
+import {Icon} from '../../../components/Icon';
 import {GLOBAL_COLORS} from '../../util/colors';
 
 export const Wrapper = styled.div<{showBindings?: boolean}>`
@@ -59,7 +59,7 @@ export const Wrapper = styled.div<{showBindings?: boolean}>`
 `;
 Wrapper.displayName = 'S.Wrapper';
 
-export const ToggleBindingsButton = styled(WBIcon)`
+export const ToggleBindingsButton: typeof Icon = styled(Icon)`
   position: absolute;
   top: 8px;
   right: 8px;
@@ -79,4 +79,3 @@ export const ToggleBindingsButton = styled(WBIcon)`
     background: white;
   }
 `;
-ToggleBindingsButton.displayName = 'S.ToggleBindingsButton';
diff --git a/weave-js/src/common/components/Vega3/CustomPanelRenderer.tsx b/weave-js/src/common/components/Vega3/CustomPanelRenderer.tsx
index c1c5ed28d5e4..0b05856df663 100644
--- a/weave-js/src/common/components/Vega3/CustomPanelRenderer.tsx
+++ b/weave-js/src/common/components/Vega3/CustomPanelRenderer.tsx
@@ -527,7 +527,7 @@ const CustomPanelRenderer: React.FC<CustomPanelRendererProps> = props => {
             )}
             {hasBindings && (
               <S.ToggleBindingsButton
-                name={showBindings ? 'close' : 'configuration'}
+                name={showBindings ? 'close' : 'settings'}
                 onClick={() => setShowBindings(s => !s)}
               />
             )}
diff --git a/weave-js/src/common/components/WBMenu.styles.ts b/weave-js/src/common/components/WBMenu.styles.ts
index 265cd2bf051f..38b6264d72fb 100644
--- a/weave-js/src/common/components/WBMenu.styles.ts
+++ b/weave-js/src/common/components/WBMenu.styles.ts
@@ -1,7 +1,7 @@
 import {foundations} from '@wandb/ui';
-import {WBIcon} from '@wandb/ui';
 import styled from 'styled-components';
 
+import {Icon} from '../../components/Icon';
 import type {WBMenuTheme} from './WBMenu';
 
 const {legacy} = foundations;
@@ -40,7 +40,7 @@ export const Item = styled.div<{
     props.hovered ? getHighlightForTheme(props.theme.main) : 'none'};
 `;
 
-export const ItemIcon: typeof WBIcon = styled(WBIcon)`
+export const ItemIcon: typeof Icon = styled(Icon)`
   margin-left: 8px;
   font-size: 16px;
   width: 16px;
diff --git a/weave-js/src/common/components/WBMenu.tsx b/weave-js/src/common/components/WBMenu.tsx
index e166442987ca..fc7a92d71358 100644
--- a/weave-js/src/common/components/WBMenu.tsx
+++ b/weave-js/src/common/components/WBMenu.tsx
@@ -4,6 +4,7 @@ import computeScrollIntoView from 'compute-scroll-into-view';
 import React, {useEffect, useMemo, useState} from 'react';
 import {ThemeProvider} from 'styled-components';
 
+import type {IconName} from '../../components/Icon';
 import * as S from './WBMenu.styles';
 
 export type WBMenuOption = {
@@ -37,20 +38,26 @@ const DEFAULT_OPTION_RENDERER: OptionRenderer = ({
   selected,
   fontSize,
   lineHeight,
-}) => (
-  <S.Item
-    data-test={option['data-test']}
-    hovered={hovered}
-    fontSize={fontSize}
-    lineHeight={lineHeight}>
-    {getOptionDisplayName(option)}
-    <S.ItemIcon
-      name={
-        option.icon ?? (selected && option.icon !== null ? 'check' : 'blank')
-      }
-    />
-  </S.Item>
-);
+}) => {
+  const iconName =
+    option.icon ?? (selected && option.icon !== null ? 'checkmark' : 'blank');
+  return (
+    <S.Item
+      data-test={option['data-test']}
+      hovered={hovered}
+      fontSize={fontSize}
+      lineHeight={lineHeight}>
+      {getOptionDisplayName(option)}
+      {iconName !== 'blank' && (
+        <S.ItemIcon
+          name={iconName as IconName}
+          hovered={hovered}
+          selected={selected}
+        />
+      )}
+    </S.Item>
+  );
+};
 
 export type OptionRenderer = (props: {
   option: WBMenuOption;
diff --git a/weave-js/src/common/components/WBSelect.tsx b/weave-js/src/common/components/WBSelect.tsx
index 091960eb0f5c..1e7b8e13dda6 100644
--- a/weave-js/src/common/components/WBSelect.tsx
+++ b/weave-js/src/common/components/WBSelect.tsx
@@ -1,7 +1,7 @@
-import {WBIcon} from '@wandb/ui';
 import {WBPopup} from '@wandb/ui';
 import React from 'react';
 
+import {Icon} from '../../components/Icon';
 import {useOnMouseDownOutside} from '../util/dom';
 import {
   getOptionDisplayName,
@@ -98,7 +98,7 @@ export const WBSelect: React.FC<WBSelectProps> = props => {
           <S.CaretWrapper
             ref={node => setOpenerEl(node)}
             onMouseDown={() => setOpen(o => !o)}>
-            <WBIcon name="next" />
+            <Icon name="chevron-next" />
           </S.CaretWrapper>
         </S.TypeableWrapper>
       ) : (
diff --git a/weave-js/src/components/Icon/Icon.tsx b/weave-js/src/components/Icon/Icon.tsx
index ea17a5509b8f..0771632bce28 100644
--- a/weave-js/src/components/Icon/Icon.tsx
+++ b/weave-js/src/components/Icon/Icon.tsx
@@ -160,10 +160,14 @@ import {ReactComponent as ImportOrchestrationLaunch} from '../../assets/icons/ic
 import {ReactComponent as ImportOrganizationCorporate} from '../../assets/icons/icon-organization-corporate.svg';
 import {ReactComponent as ImportOverflowHorizontal} from '../../assets/icons/icon-overflow-horizontal.svg';
 import {ReactComponent as ImportOverflowVertical} from '../../assets/icons/icon-overflow-vertical.svg';
+import {ReactComponent as ImportOverlay2Column} from '../../assets/icons/icon-overlay-2-column.svg';
+import {ReactComponent as ImportOverlay3Column} from '../../assets/icons/icon-overlay-3-column.svg';
+import {ReactComponent as ImportOverlayStack} from '../../assets/icons/icon-overlay-stack.svg';
 import {ReactComponent as ImportPanTool} from '../../assets/icons/icon-pan-tool.svg';
 import {ReactComponent as ImportPanTool1} from '../../assets/icons/icon-pan-tool-1.svg';
 import {ReactComponent as ImportPanel} from '../../assets/icons/icon-panel.svg';
 import {ReactComponent as ImportPanelAutoGen} from '../../assets/icons/icon-panel-auto-gen.svg';
+import {ReactComponent as ImportPanelImages} from '../../assets/icons/icon-panel-images.svg';
 import {ReactComponent as ImportPanelManual} from '../../assets/icons/icon-panel-manual.svg';
 import {ReactComponent as ImportParallelCoordinates} from '../../assets/icons/icon-parallel-coordinates.svg';
 import {ReactComponent as ImportParentBackUp} from '../../assets/icons/icon-parent-back-up.svg';
@@ -777,6 +781,15 @@ export const IconOverflowHorizontal = (props: SVGIconProps) => (
 export const IconOverflowVertical = (props: SVGIconProps) => (
   <ImportOverflowVertical {...updateIconProps(props)} />
 );
+export const IconOverlay2Column = (props: SVGIconProps) => (
+  <ImportOverlay2Column {...updateIconProps(props)} />
+);
+export const IconOverlay3Column = (props: SVGIconProps) => (
+  <ImportOverlay3Column {...updateIconProps(props)} />
+);
+export const IconOverlayStack = (props: SVGIconProps) => (
+  <ImportOverlayStack {...updateIconProps(props)} />
+);
 export const IconPanTool = (props: SVGIconProps) => (
   <ImportPanTool {...updateIconProps(props)} />
 );
@@ -789,6 +802,9 @@ export const IconPanel = (props: SVGIconProps) => (
 export const IconPanelAutoGen = (props: SVGIconProps) => (
   <ImportPanelAutoGen {...updateIconProps(props)} />
 );
+export const IconPanelImages = (props: SVGIconProps) => (
+  <ImportPanelImages {...updateIconProps(props)} />
+);
 export const IconPanelManual = (props: SVGIconProps) => (
   <ImportPanelManual {...updateIconProps(props)} />
 );
@@ -1296,10 +1312,14 @@ const ICON_NAME_TO_ICON: Record<IconName, ElementType> = {
   'organization-corporate': IconOrganizationCorporate,
   'overflow-horizontal': IconOverflowHorizontal,
   'overflow-vertical': IconOverflowVertical,
+  'overlay-2-column': IconOverlay2Column,
+  'overlay-3-column': IconOverlay3Column,
+  'overlay-stack': IconOverlayStack,
   'pan-tool': IconPanTool,
   'pan-tool-1': IconPanTool1,
   panel: IconPanel,
   'panel-auto-gen': IconPanelAutoGen,
+  'panel-images': IconPanelImages,
   'panel-manual': IconPanelManual,
   'parallel-coordinates': IconParallelCoordinates,
   'parent-back-up': IconParentBackUp,
diff --git a/weave-js/src/components/Icon/index.ts b/weave-js/src/components/Icon/index.ts
index 8aa819c4c948..097194b5853e 100644
--- a/weave-js/src/components/Icon/index.ts
+++ b/weave-js/src/components/Icon/index.ts
@@ -160,8 +160,12 @@ export {
   IconOrganizationCorporate,
   IconOverflowHorizontal,
   IconOverflowVertical,
+  IconOverlay2Column,
+  IconOverlay3Column,
+  IconOverlayStack,
   IconPanel,
   IconPanelAutoGen,
+  IconPanelImages,
   IconPanelManual,
   IconPanTool,
   IconPanTool1,
diff --git a/weave-js/src/components/Icon/types.ts b/weave-js/src/components/Icon/types.ts
index a2a8a6f09dff..833e0db57eff 100644
--- a/weave-js/src/components/Icon/types.ts
+++ b/weave-js/src/components/Icon/types.ts
@@ -159,10 +159,14 @@ export const IconNames = {
   OrganizationCorporate: 'organization-corporate',
   OverflowHorizontal: 'overflow-horizontal',
   OverflowVertical: 'overflow-vertical',
+  Overlay2Column: 'overlay-2-column',
+  Overlay3Column: 'overlay-3-column',
+  OverlayStack: 'overlay-stack',
   PanTool: 'pan-tool',
   PanTool1: 'pan-tool-1',
   Panel: 'panel',
   PanelAutoGen: 'panel-auto-gen',
+  PanelImages: 'panel-images',
   PanelManual: 'panel-manual',
   ParallelCoordinates: 'parallel-coordinates',
   ParentBackUp: 'parent-back-up',
diff --git a/weave-js/src/components/Panel2/ControlImageOverlays.tsx b/weave-js/src/components/Panel2/ControlImageOverlays.tsx
index dae7d3a137ed..58160352a00e 100644
--- a/weave-js/src/components/Panel2/ControlImageOverlays.tsx
+++ b/weave-js/src/components/Panel2/ControlImageOverlays.tsx
@@ -1,4 +1,3 @@
-import {WBIcon} from '@wandb/ui';
 import HelpPopup from '@wandb/weave/common/components/elements/HelpPopup';
 import {BoundingBoxSliderControl} from '@wandb/weave/common/components/MediaCard';
 import {ShowMoreContainer} from '@wandb/weave/common/components/showMoreContainer';
@@ -10,6 +9,7 @@ import * as _ from 'lodash';
 import React, {useEffect, useMemo, useState} from 'react';
 import {Button, ButtonGroup} from 'semantic-ui-react';
 
+import {Icon} from '../Icon';
 import {ControlsBox} from './ControlBox';
 import * as S from './ControlImageOverlays.styles';
 import {ControlsMask} from './ControlMask';
@@ -189,7 +189,7 @@ const TileLayoutButtons: React.FC<TileLayoutButtonsProps> = ({
             'action-button--active': tileLayout === 'ALL_STACKED',
           })}
           onClick={() => setLayoutType('ALL_STACKED')}>
-          <WBIcon name="overlay-stack" />
+          <Icon name="overlay-stack" />
         </Button>
         <Button
           size="tiny"
@@ -198,7 +198,7 @@ const TileLayoutButtons: React.FC<TileLayoutButtonsProps> = ({
             'action-button--active': tileLayout === 'MASKS_NEXT_TO_IMAGE',
           })}
           onClick={() => setLayoutType('MASKS_NEXT_TO_IMAGE')}>
-          <WBIcon name={'overlay-2-column'} />
+          <Icon name={'overlay-2-column'} />
         </Button>
         {maskCount > 1 && (
           <Button
@@ -208,7 +208,7 @@ const TileLayoutButtons: React.FC<TileLayoutButtonsProps> = ({
               'action-button--active': tileLayout === 'ALL_SPLIT',
             })}
             onClick={() => setLayoutType('ALL_SPLIT')}>
-            <WBIcon name="overlay-3-column" />
+            <Icon name="overlay-3-column" />
           </Button>
         )}
       </ButtonGroup>
diff --git a/weave-js/src/components/Panel2/ControlMask.tsx b/weave-js/src/components/Panel2/ControlMask.tsx
index 963f5f1914bd..4c4576b3e2f6 100644
--- a/weave-js/src/components/Panel2/ControlMask.tsx
+++ b/weave-js/src/components/Panel2/ControlMask.tsx
@@ -1,6 +1,6 @@
-import {WBIcon} from '@wandb/ui';
 import React from 'react';
 
+import {Icon} from '../Icon';
 import {ControlsImageOverlaysControls} from './ControlImageOverlays';
 import * as Controls from './controlsImage';
 import {DEFAULT_TILE_LAYOUT, defaultHideImageState} from './ImageWithOverlays';
@@ -36,7 +36,7 @@ export const ControlsMask: React.FC<ControlMaskProps> = ({
 
   return (
     <span>
-      <WBIcon
+      <Icon
         style={{
           fontSize: '1.1em',
           cursor: 'pointer',
diff --git a/weave-js/src/components/Panel2/ControlPage.styles.ts b/weave-js/src/components/Panel2/ControlPage.styles.ts
index 3cafbaec7d8b..de83138e7045 100644
--- a/weave-js/src/components/Panel2/ControlPage.styles.ts
+++ b/weave-js/src/components/Panel2/ControlPage.styles.ts
@@ -1,7 +1,8 @@
-import {WBIcon} from '@wandb/ui';
 import * as globals from '@wandb/weave/common/css/globals.styles';
 import styled from 'styled-components';
 
+import {Icon} from '../Icon';
+
 export const ControlBar = styled.div`
   height: 1.7em;
   border-top: 1px solid #ddd;
@@ -10,7 +11,7 @@ export const ControlBar = styled.div`
   justify-content: space-between;
 `;
 
-export const ArrowIcon = styled(WBIcon)`
+export const ArrowIcon: typeof Icon = styled(Icon)`
   cursor: pointer;
   height: 100%;
   padding: 4px 0px 0px 0px;
diff --git a/weave-js/src/components/Panel2/ControlPage.tsx b/weave-js/src/components/Panel2/ControlPage.tsx
index 571ac193bfcf..61c4e2cd50bb 100644
--- a/weave-js/src/components/Panel2/ControlPage.tsx
+++ b/weave-js/src/components/Panel2/ControlPage.tsx
@@ -50,7 +50,7 @@ const PageControls: React.FC<{
     <></>
   ) : (
     <S.ControlBar>
-      <S.ArrowIcon name="previous" onClick={prevPage} />
+      <S.ArrowIcon name="chevron-back" onClick={prevPage} />
       <span
         style={{
           flex: '1 1 auto',
@@ -59,7 +59,7 @@ const PageControls: React.FC<{
         {startIndex + 1}
         {!singleItem && `-${endIndex}`} of {totalItems}{' '}
       </span>
-      <S.ArrowIcon name="next" onClick={nextPage} />
+      <S.ArrowIcon name="chevron-next" onClick={nextPage} />
     </S.ControlBar>
   );
 };
diff --git a/weave-js/src/components/Panel2/PanelTable.styles.ts b/weave-js/src/components/Panel2/PanelTable.styles.ts
index ff334aaede5c..42620d3088af 100644
--- a/weave-js/src/components/Panel2/PanelTable.styles.ts
+++ b/weave-js/src/components/Panel2/PanelTable.styles.ts
@@ -1,9 +1,9 @@
-import {WBIcon} from '@wandb/ui';
 import * as globals from '@wandb/weave/common/css/globals.styles';
 import styled from 'styled-components';
 
 import {OBLIVION} from '../../common/css/color.styles';
 import {hexToRGB} from '../../common/css/utils';
+import {Icon} from '../Icon';
 
 export const ColumnHeader = styled.div`
   display: flex;
@@ -86,7 +86,7 @@ export const IndexColumnDrag = styled.div`
 `;
 IndexColumnDrag.displayName = 'S.IndexColumnDrag';
 
-export const FilterIcon = styled(WBIcon)`
+export const FilterIcon: typeof Icon = styled(Icon)`
   cursor: pointer;
   color: ${globals.primary};
   :hover {
@@ -94,7 +94,6 @@ export const FilterIcon = styled(WBIcon)`
     border-radius: 2px;
   }
 `;
-FilterIcon.displayName = 'S.FilterIcon';
 
 export const ColumnAction = styled.div<{isHovered?: boolean}>`
   cursor: pointer;
@@ -138,14 +137,7 @@ export const TableAction = styled.div<{
 `;
 TableAction.displayName = 'S.TableAction';
 
-export const EllipsisIcon = styled(WBIcon)`
-  width: 100%;
-  height: 100%;
-  padding-top: 4px;
-`;
-EllipsisIcon.displayName = 'S.EllipsisIcon';
-
-export const TableIcon = styled(WBIcon)<{highlight?: boolean}>`
+export const TableIcon: typeof Icon = styled(Icon)<{highlight?: boolean}>`
   cursor: pointer;
   padding: 2px 0px 0px 0px;
   :hover {
@@ -155,16 +147,14 @@ export const TableIcon = styled(WBIcon)<{highlight?: boolean}>`
     border-radius: 2px;
   }
 `;
-TableIcon.displayName = 'S.TableIcon';
 
-export const ControlIcon = styled(WBIcon)`
+export const ControlIcon: typeof Icon = styled(Icon)`
   cursor: pointer;
   color: ${globals.primary};
   margin: auto;
 `;
-ControlIcon.displayName = 'S.ControlIcon';
 
-export const SortIcon = styled(WBIcon)`
+export const SortIcon: typeof Icon = styled(Icon)`
   cursor: pointer;
   color: #afafaf;
   :hover {
@@ -172,7 +162,6 @@ export const SortIcon = styled(WBIcon)`
     border-radius: 2px;
   }
 `;
-SortIcon.displayName = 'S.SortIcon';
 
 export const ColumnEditorSection = styled.div`
   margin-bottom: 24px;
diff --git a/weave-js/src/components/Panel2/PanelTable/ColumnHeader.tsx b/weave-js/src/components/Panel2/PanelTable/ColumnHeader.tsx
index 612131a87b2b..7c23dfa325fc 100644
--- a/weave-js/src/components/Panel2/PanelTable/ColumnHeader.tsx
+++ b/weave-js/src/components/Panel2/PanelTable/ColumnHeader.tsx
@@ -19,7 +19,6 @@ import {
   opCount,
   voidNode,
 } from '@wandb/weave/core';
-import {TableState} from '@wandb/weave/index';
 import React, {useCallback, useContext, useMemo, useState} from 'react';
 import {Popup} from 'semantic-ui-react';
 
@@ -29,6 +28,7 @@ import {useWeaveContext} from '../../../context';
 import {focusEditor, WeaveExpression} from '../../../panel/WeaveExpression';
 import {SUGGESTION_OPTION_CLASS} from '../../../panel/WeaveExpression/styles';
 import {Button} from '../../Button';
+import {Icon, type IconName} from '../../Icon';
 import {Tooltip} from '../../Tooltip';
 import {usePanelStacksForType} from '../availablePanels';
 import * as ExpressionView from '../ExpressionView';
@@ -344,7 +344,7 @@ export const ColumnHeader: React.FC<{
     menuItems.push({
       value: 'settings',
       name: 'Edit cell expression',
-      icon: 'configuration',
+      icon: 'settings',
       onSelect: () => openColumnSettings(),
     });
     menuItems.push(makeMenuItemDivider('expression-div'));
@@ -365,7 +365,7 @@ export const ColumnHeader: React.FC<{
       menuItems.push({
         value: 'group',
         name: 'Group by',
-        icon: 'group-runs',
+        icon: 'group',
         onSelect: async () => {
           recordEvent('GROUP');
           let newTableState: Table.TableState | null = null;
@@ -402,7 +402,7 @@ export const ColumnHeader: React.FC<{
       menuItems.push({
         value: 'ungroup',
         name: 'Ungroup',
-        icon: 'group-runs',
+        icon: 'group',
         onSelect: doUngroup,
       });
     }
@@ -419,7 +419,7 @@ export const ColumnHeader: React.FC<{
         {
           value: 'insert-right',
           name: 'Insert 1 right',
-          icon: 'next',
+          icon: 'chevron-next',
           onSelect: () => {
             const newTableState = Table.insertColumnRight(
               tableState,
@@ -434,7 +434,7 @@ export const ColumnHeader: React.FC<{
         {
           value: 'insert-left',
           name: 'Insert 1 left',
-          icon: 'previous',
+          icon: 'chevron-back',
           onSelect: () => {
             const newTableState = Table.insertColumnLeft(
               tableState,
@@ -493,7 +493,7 @@ export const ColumnHeader: React.FC<{
         {
           value: 'remove-all-right',
           name: 'Remove to the right',
-          icon: 'next',
+          icon: 'chevron-next',
           onSelect: () => {
             const newTableState = Table.removeColumnsToRight(
               tableState,
@@ -507,7 +507,7 @@ export const ColumnHeader: React.FC<{
         {
           value: 'remove-all-left',
           name: 'Remove to the left',
-          icon: 'previous',
+          icon: 'chevron-back',
           onSelect: () => {
             const newTableState = Table.removeColumnsToLeft(
               tableState,
@@ -778,8 +778,8 @@ export const ColumnHeader: React.FC<{
                   )}
                   {isGroupCol && (
                     <S.ControlIcon
-                      name="group-runs"
-                      onClick={e => {
+                      name="group"
+                      onClick={() => {
                         recordEvent('REMOVE_COLUMN_GROUPING');
                         doUngroup();
                       }}
@@ -797,11 +797,10 @@ export const ColumnHeader: React.FC<{
                     />
                   )}
                 </S.ColumnAction>
-                <S.ColumnAction>
-                  <S.EllipsisIcon
-                    ref={anchorRef}
+                <S.ColumnAction ref={anchorRef}>
+                  <Icon
                     data-test="column-options"
-                    name="overflow"
+                    name="overflow-vertical"
                     className="column-actions-trigger"
                     onClick={() => setOpen(o => !o)}
                   />
@@ -826,8 +825,8 @@ const SortStateToggle: React.FC<{
   if (colSortState && colSortState === 'desc') {
     return (
       <S.ControlIcon
-        name="down-arrow"
-        onClick={async e => {
+        name="sort-descending"
+        onClick={async () => {
           recordEvent('REMOVE_COLUMN_SORT');
           updateTableState(Table.disableSortByCol(tableState, colId));
         }}
@@ -836,8 +835,8 @@ const SortStateToggle: React.FC<{
   } else if (colSortState && colSortState === 'asc') {
     return (
       <S.ControlIcon
-        name="up-arrow"
-        onClick={async e => {
+        name="sort-ascending"
+        onClick={async () => {
           recordEvent('UPDATE_COLUMN_SORT_DESC');
           updateTableState(
             Table.enableSortByCol(Table.disableSort(tableState), colId, false)
@@ -868,15 +867,22 @@ const ColumnMenuOptionRenderer: OptionRenderer = ({
   option,
   hovered,
   selected,
-}) => (
-  <Item
-    data-test={option['data-test']}
-    hovered={hovered}
-    style={{justifyContent: 'flex-start'}}>
-    <ItemIcon
-      style={{marginRight: '8px', marginLeft: 0}}
-      name={option.icon ?? (selected && option.icon ? 'check' : 'blank')}
-    />
-    {option.name ?? option.value}
-  </Item>
-);
+}) => {
+  const iconName = option.icon ?? (selected && option.icon ? 'check' : 'blank');
+
+  return (
+    <Item
+      data-test={option['data-test']}
+      hovered={hovered}
+      style={{justifyContent: 'flex-start'}}>
+      {iconName !== 'blank' && (
+        <ItemIcon
+          style={{marginRight: '8px', marginLeft: 0}}
+          name={iconName as IconName}
+        />
+      )}
+
+      {option.name ?? option.value}
+    </Item>
+  );
+};


Reviews:
user: login: domphan-wandb
body: I think the new elipsis icon looks a bit strange in the header. Is there any way to use the old one?
<img width="240" alt="image" src="https://github.com/user-attachments/assets/178871ad-7e8f-44f0-893e-3eeccca9c06a" />

I'm not exactly sure what's causing this, but when I'm opening the column menu, it's being anchored in the corner of the page:
<img width="1530" alt="image" src="https://github.com/user-attachments/assets/6c0f1f4b-441c-43e5-a495-920365651d83" />

For reference, here's what it looked like before:
<img width="307" alt="image" src="https://github.com/user-attachments/assets/52dc1798-2499-40c1-8c83-6c022041e4c6" />


state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-27T19:25:00Z
user: login: onx2
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-27T20:03:18Z
user: login: onx2
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-27T20:24:46Z
user: login: domphan-wandb
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-27T20:47:55Z
user: login: onx2
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-27T20:52:45Z
user: login: domphan-wandb
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-27T20:53:28Z
user: login: domphan-wandb
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-27T20:53:39Z
user: login: ktmathews89
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-28T18:19:48Z
user: login: ktmathews89
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-28T18:22:43Z
user: login: ktmathews89
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-28T18:51:18Z
user: login: onx2
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-28T19:10:19Z
user: login: ktmathews89
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-28T19:13:29Z
user: login: ktmathews89
body: after that one `iconName` fix... you should be good to go!
state: APPROVED
author_association: NONE
submitted_at: 2025-05-28T19:14:56Z

Review comments:
diff_hunk: @@ -527,7 +527,7 @@ const CustomPanelRenderer: React.FC<CustomPanelRendererProps> = props => {
             )}
             {hasBindings && (
               <S.ToggleBindingsButton
-                name={showBindings ? 'close' : 'configuration'}
+                name={showBindings ? 'close' : 'settings'}
path: weave-js/src/common/components/Vega3/CustomPanelRenderer.tsx
user: login: domphan-wandb
body: just curious, does Icon have a different set of icons compared to WBIcon?
created_at: 2025-05-27T18:54:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -873,10 +872,9 @@ const ColumnMenuOptionRenderer: OptionRenderer = ({
     data-test={option['data-test']}
     hovered={hovered}
     style={{justifyContent: 'flex-start'}}>
-    <ItemIcon
-      style={{marginRight: '8px', marginLeft: 0}}
-      name={option.icon ?? (selected && option.icon ? 'check' : 'blank')}
-    />
+    {option.icon && selected && (
+      <ItemIcon style={{marginRight: '8px', marginLeft: 0}} name="checkmark" />
path: weave-js/src/components/Panel2/PanelTable/ColumnHeader.tsx
user: login: domphan-wandb
body: ```suggestion
      <ItemIcon style={{marginRight: '8px', marginLeft: 0}} name={option.icon ?? "checkmark"} />
```
created_at: 2025-05-27T19:23:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -527,7 +527,7 @@ const CustomPanelRenderer: React.FC<CustomPanelRendererProps> = props => {
             )}
             {hasBindings && (
               <S.ToggleBindingsButton
-                name={showBindings ? 'close' : 'configuration'}
+                name={showBindings ? 'close' : 'settings'}
path: weave-js/src/common/components/Vega3/CustomPanelRenderer.tsx
user: login: onx2
body: yup, they are separate component libraries. unfortunately icons have evolved over the years and we never kept up with that across the app so these changes are more involved
created_at: 2025-05-27T20:03:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -873,10 +872,9 @@ const ColumnMenuOptionRenderer: OptionRenderer = ({
     data-test={option['data-test']}
     hovered={hovered}
     style={{justifyContent: 'flex-start'}}>
-    <ItemIcon
-      style={{marginRight: '8px', marginLeft: 0}}
-      name={option.icon ?? (selected && option.icon ? 'check' : 'blank')}
-    />
+    {option.icon && selected && (
+      <ItemIcon style={{marginRight: '8px', marginLeft: 0}} name="checkmark" />
path: weave-js/src/components/Panel2/PanelTable/ColumnHeader.tsx
user: login: onx2
body: I have that check above `{option.icon && selected && (`, I think the previous logic was when there is an icon passed in check to see if the item is selected, render a checkmark. Otherwise render nothing "blank" or "undefined" if there is no icon.
created_at: 2025-05-27T20:24:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -873,10 +872,9 @@ const ColumnMenuOptionRenderer: OptionRenderer = ({
     data-test={option['data-test']}
     hovered={hovered}
     style={{justifyContent: 'flex-start'}}>
-    <ItemIcon
-      style={{marginRight: '8px', marginLeft: 0}}
-      name={option.icon ?? (selected && option.icon ? 'check' : 'blank')}
-    />
+    {option.icon && selected && (
+      <ItemIcon style={{marginRight: '8px', marginLeft: 0}} name="checkmark" />
path: weave-js/src/components/Panel2/PanelTable/ColumnHeader.tsx
user: login: domphan-wandb
body: I think it might be necessary because the latest screenshot shows only the trash icon. Each menu item should have an icon to the left of it.
created_at: 2025-05-27T20:47:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -873,10 +872,9 @@ const ColumnMenuOptionRenderer: OptionRenderer = ({
     data-test={option['data-test']}
     hovered={hovered}
     style={{justifyContent: 'flex-start'}}>
-    <ItemIcon
-      style={{marginRight: '8px', marginLeft: 0}}
-      name={option.icon ?? (selected && option.icon ? 'check' : 'blank')}
-    />
+    {option.icon && selected && (
+      <ItemIcon style={{marginRight: '8px', marginLeft: 0}} name="checkmark" />
path: weave-js/src/components/Panel2/PanelTable/ColumnHeader.tsx
user: login: onx2
body: oh okay I see whats happening. I must have forgotten to add this from a different PR 😅 

I updated and now we have:
<img width="377" alt="image" src="https://github.com/user-attachments/assets/2c32657c-40fc-42b8-908d-3f3713d890c4" />

created_at: 2025-05-27T20:52:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -873,10 +872,9 @@ const ColumnMenuOptionRenderer: OptionRenderer = ({
     data-test={option['data-test']}
     hovered={hovered}
     style={{justifyContent: 'flex-start'}}>
-    <ItemIcon
-      style={{marginRight: '8px', marginLeft: 0}}
-      name={option.icon ?? (selected && option.icon ? 'check' : 'blank')}
-    />
+    {option.icon && selected && (
+      <ItemIcon style={{marginRight: '8px', marginLeft: 0}} name="checkmark" />
path: weave-js/src/components/Panel2/PanelTable/ColumnHeader.tsx
user: login: domphan-wandb
body: Sweet thanks!!
created_at: 2025-05-27T20:53:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -155,24 +147,21 @@ export const TableIcon = styled(WBIcon)<{highlight?: boolean}>`
     border-radius: 2px;
   }
 `;
-TableIcon.displayName = 'S.TableIcon';
path: weave-js/src/components/Panel2/PanelTable.styles.ts
user: login: ktmathews89
body: are these used for anything?
created_at: 2025-05-28T18:19:47Z
author_association: NONE
diff_hunk: @@ -873,10 +872,13 @@ const ColumnMenuOptionRenderer: OptionRenderer = ({
     data-test={option['data-test']}
     hovered={hovered}
     style={{justifyContent: 'flex-start'}}>
-    <ItemIcon
-      style={{marginRight: '8px', marginLeft: 0}}
-      name={option.icon ?? (selected && option.icon ? 'check' : 'blank')}
-    />
+    {option.icon && (
+      <ItemIcon
+        style={{marginRight: '8px', marginLeft: 0}}
+        // @todo: fix this type by updating wandb/ui local implementation
+        name={selected ? 'checkmark' : (option.icon as IconName)}
+      />
+    )}
path: weave-js/src/components/Panel2/PanelTable/ColumnHeader.tsx
user: login: ktmathews89
body: wondering if this renders the same thing...
created_at: 2025-05-28T18:22:43Z
author_association: NONE
diff_hunk: @@ -44,11 +44,7 @@ const DEFAULT_OPTION_RENDERER: OptionRenderer = ({
     fontSize={fontSize}
     lineHeight={lineHeight}>
     {getOptionDisplayName(option)}
-    <S.ItemIcon
-      name={
-        option.icon ?? (selected && option.icon !== null ? 'check' : 'blank')
-      }
-    />
+    {option.icon && selected && <S.ItemIcon name="checkmark" />}
path: weave-js/src/common/components/WBMenu.tsx
user: login: ktmathews89
body: Not sure this is equivalent 
created_at: 2025-05-28T18:51:18Z
author_association: NONE
diff_hunk: @@ -37,20 +38,26 @@ const DEFAULT_OPTION_RENDERER: OptionRenderer = ({
   selected,
   fontSize,
   lineHeight,
-}) => (
-  <S.Item
-    data-test={option['data-test']}
-    hovered={hovered}
-    fontSize={fontSize}
-    lineHeight={lineHeight}>
-    {getOptionDisplayName(option)}
-    <S.ItemIcon
-      name={
-        option.icon ?? (selected && option.icon !== null ? 'check' : 'blank')
-      }
-    />
-  </S.Item>
-);
+}) => {
+  const iconName =
+    option.icon ?? (selected && option.icon !== null ? 'checkmark' : 'blank');
+  return (
+    <S.Item
+      data-test={option['data-test']}
+      hovered={hovered}
+      fontSize={fontSize}
+      lineHeight={lineHeight}>
+      {getOptionDisplayName(option)}
+      {iconName !== 'blank' && (
+        <S.ItemIcon
+          name={iconName as IconName}
+          hovered={hovered}
+          selected={selected}
+        />
+      )}
+    </S.Item>
+  );
+};
path: weave-js/src/common/components/WBMenu.tsx
user: login: onx2
body: Visually & logically this should be idential but with the caveat that we are explicitly rendering nothing instead of `blank` icon which renders nothing.

changes here are:
- use new Icon component
- only render when _not_ blank, this is because we want to avoid sentry alerts for "blank" not being a real icon. The old system would render null but not report on it.
- coalesce the iconName into `IconName`... this isn't great but a way to get passed a larger issue
created_at: 2025-05-28T19:10:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -868,15 +867,23 @@ const ColumnMenuOptionRenderer: OptionRenderer = ({
   option,
   hovered,
   selected,
-}) => (
-  <Item
-    data-test={option['data-test']}
-    hovered={hovered}
-    style={{justifyContent: 'flex-start'}}>
-    <ItemIcon
-      style={{marginRight: '8px', marginLeft: 0}}
-      name={option.icon ?? (selected && option.icon ? 'check' : 'blank')}
-    />
-    {option.name ?? option.value}
-  </Item>
-);
+}) => {
+  const iconName =
+    option.icon ?? (selected && option.icon !== null ? 'checkmark' : 'blank');
path: weave-js/src/components/Panel2/PanelTable/ColumnHeader.tsx
user: login: ktmathews89
body: wrong name statement... need to remove the !== null check
created_at: 2025-05-28T19:13:29Z
author_association: NONE

PR comments:
user: login: circle-job-mirror[bot]
created_at: 2025-05-27T15:18:26Z
body: Preview this PR with FeatureBee: <https://beta.wandb.ai/?betaVersion=27eb3428aac95c6f9cd8d0406ff990283622292a>
author_association: NONE
user: login: onx2
created_at: 2025-05-27T20:22:09Z
body: > I think the new elipsis icon looks a bit strange in the header. Is there any way to use the old one? 
> 
> 
> For reference, here's what it looked like before: 

Sorry about that! I committed a fix for it -- here's visual now
<img width="291" alt="image" src="https://github.com/user-attachments/assets/cabab9dc-c623-4df9-832c-ebfa23614adc" />

author_association: CONTRIBUTOR
user: login: domphan-wandb
created_at: 2025-05-27T20:48:15Z
body: > > I think the new elipsis icon looks a bit strange in the header. Is there any way to use the old one?
> > For reference, here's what it looked like before:
> 
> Sorry about that! I committed a fix for it -- here's visual now <img alt="image" width="291" src="https://private-user-images.githubusercontent.com/23039725/448100636-cabab9dc-c623-4df9-832c-ebfa23614adc.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDgzNzc5OTksIm5iZiI6MTc0ODM3NzY5OSwicGF0aCI6Ii8yMzAzOTcyNS80NDgxMDA2MzYtY2FiYWI5ZGMtYzYyMy00ZGY5LTgzMmMtZWJmYTIzNjE0YWRjLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTA1MjclMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwNTI3VDIwMjgxOVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWNiNmZjN2I2ZTMxMTdkZjJjZGM3ZmUxMjM5ODg1ZjhmMzBjMDM0NWRmZTE2MzRlNWRjYjFiMGZiOGFjN2I5NDAmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.kN8faBShw2Q9oXo7zrPsG_MJ2rUzWn_DJ8ecaTNgSn8">

thanks for much for fixing the positioning on it!
author_association: CONTRIBUTOR