diff --git a/.github/workflows/reusable-workflows.yml b/.github/workflows/reusable-workflows.yml
index b517200783..78d10e88c3 100644
--- a/.github/workflows/reusable-workflows.yml
+++ b/.github/workflows/reusable-workflows.yml
@@ -2,6 +2,7 @@ name: Reusable Workflows
on:
pull_request:
+ types: [opened, reopened, synchronize, edited]
jobs:
pr-title-check:
diff --git a/docs/components/Data Display/Avatar/Documentation.mdx b/docs/components/Data Display/Avatar/Documentation.mdx
index 77bed11116..afd062b9ef 100644
--- a/docs/components/Data Display/Avatar/Documentation.mdx
+++ b/docs/components/Data Display/Avatar/Documentation.mdx
@@ -6,18 +6,31 @@ import * as AvatarStories from '../../../../src/components/data-display/Avatar/A
# Avatar
-#### Overview
+#### Shape
-The **Avatar** component is used to display a profile image, initials, or an icon, commonly for identification purposes.
+The default avatar is circular. Switch the `shape` prop to `square` when you need hard edges or when aligning with adjacent rectangular content.
-#### When to use
+
-- To represent an account, organization, or workspace.
-- To represent a single user or as a placeholder when a user profile image isn’t available.
+#### Sizes
-#### Current Usage
+
-- **[Navigation](https://mparticle.github.io/aquarium/?path=/story/components-navigation-globalnavigation--primary)** – Avatar is used within the navigation sidebar to represent the selected workspace.
+#### Photo avatar
+
+
+
+#### Icon avatar
+
+
+
+#### Initial avatar
+
+
+
+#### Badge avatars
+
+
#### Related Links
@@ -26,7 +39,3 @@ The **Avatar** component is used to display a profile image, initials, or an ico
| Eames | [Avatar Component](https://www.figma.com/design/veXnmignQnJz8StIq10VJ5/Eames-2.0---Foundations-%26-Components?node-id=397-12044&node-type=canvas&t=B6HJWqqDsUOypZQj-0) |
| AntD | [Avatar Component](https://ant.design/components/avatar) |
-
-
-
-
diff --git a/docs/components/Data Display/Badge/Documentation.mdx b/docs/components/Data Display/Badge/Documentation.mdx
index 96aa428d1d..4146bb68e2 100644
--- a/docs/components/Data Display/Badge/Documentation.mdx
+++ b/docs/components/Data Display/Badge/Documentation.mdx
@@ -12,23 +12,21 @@ import * as BadgeStories from '../../../../src/components/data-display/Badge/Bad
The **Badge** component provides visual indicators for an item’s status, available in two styles: a **dot** and a \*\*status badge" with text.
-### [Dot Badge](https://mparticle.github.io/aquarium/?path=/story/components-data-display-badge--dot-badge)
+#### Status badge
-#### When to use
+Use status badges to indicate where an item sits in a process with a clear beginning and end. Pair the dot with concise labels so the current state is obvious without extra context.
-Use **Badge** when a status needs to be displayed next to an element in a compact form.
+
-#### Current Usages
+#### Dot badge
-- **[Navigation](https://mparticle.github.io/aquarium/?path=/story/components-navigation-globalnavigation--primary)** – Badge is used within the navigation sidebar for notification use cases.
+Use dot badges to surface the current status of an item without additional copy. Each dot maps to the core status tokens, including the paused state for work that is temporarily on hold.
-#### Related Links
+#### Related links
| Type | Resource |
| ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Eames | [Badge Component](https://www.figma.com/design/veXnmignQnJz8StIq10VJ5/Eames-2.0---Foundations-%26-Components?node-id=399-0&node-type=canvas&t=B6HJWqqDsUOypZQj-0) |
| AntD | [Badge Component](https://ant.design/components/badge) |
-
-
diff --git a/docs/components/Data Display/Popover/Documentation.mdx b/docs/components/Data Display/Popover/Documentation.mdx
index 24c9d6194f..750ae96235 100644
--- a/docs/components/Data Display/Popover/Documentation.mdx
+++ b/docs/components/Data Display/Popover/Documentation.mdx
@@ -1,6 +1,6 @@
import { Meta, Canvas } from '@storybook/blocks'
-import * as PopoverStories from '../../../../src/components/data-display/Popover/Popover.stories'
+import PopoverStories, { Primary as PopoverPrimary } from '../../../../src/components/data-display/Popover/Popover.stories'
@@ -34,4 +34,4 @@ Use [Tooltip](https://mparticle.github.io/aquarium/?path=/story/components-data-
| AntD | [Popover Component](https://ant.design/components/popover) |
-
+
diff --git a/docs/components/Data Display/Segmented/Documentation.mdx b/docs/components/Data Display/Segmented/Documentation.mdx
index 2ab4a2ff64..9604bbbae3 100644
--- a/docs/components/Data Display/Segmented/Documentation.mdx
+++ b/docs/components/Data Display/Segmented/Documentation.mdx
@@ -4,12 +4,26 @@ import * as SegmentedStories from '../../../../src/components/data-display/Segme
-{/* Documentation goes here */}
-
# Segmented
-This is the documentation for the Segmenteds component
+#### Overview
+
+The **Segmented** component presents a compact set of mutually exclusive choices. Each option is easy to scan and the selected value is highlighted to confirm the current state.
+
+#### Small segmented control
+
+Use the small size when the control sits inside dense layouts such as tables or cards with limited space.
+
+
+
+#### Large segmented control
+
+Use the large size for standalone toggles where the segment labels need more breathing room and legibility.
+
+
-{/* Documentation goes here */}
+#### Related links
-
+| Type | Resource |
+| ---- | -------- |
+| AntD | [Segmented Component](https://ant.design/components/segmented) |
diff --git a/docs/components/Data Entry/Checkbox/Documentation.mdx b/docs/components/Data Entry/Checkbox/Documentation.mdx
index 922e5016b9..d14aeb07dd 100644
--- a/docs/components/Data Entry/Checkbox/Documentation.mdx
+++ b/docs/components/Data Entry/Checkbox/Documentation.mdx
@@ -19,16 +19,38 @@ Use the **Checkbox** component when:
- For binary options, especially when changes require saving.
- For active agreements, such as accepting terms of service.
-When selecting a single option from a set of mutually exclusive choices, use **[Radio Buttons](https://mparticle.github.io/aquarium/?path=/story/components-data-entry-radio--primary)**.
+### Default checkbox
-Use **[switch](https://mparticle.github.io/aquarium/?path=/story/components-data-entry-switch--primary)** for cases like activation, filter controls, or subscriptions where there is a clear "on/off" state.
+Start with the standard checkbox for simple agree-or-disagree prompts.
-#### Related Links
+
-| Type | Resource |
-| ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| Eames | [Checkbox Component](https://www.figma.com/design/veXnmignQnJz8StIq10VJ5/Eames-2.0---Foundations-%26-Components?node-id=388-11036&p=f&t=zURbLpG60aM6aJiH-0) |
-| AntD | [Checkbox Component](https://ant.design/components/checkbox) |
+### Checkbox states
+Reference this overview to show each visual state side by side—interactive default, checked, indeterminate, and disabled.
-
+
+
+### Checkbox groups
+
+Group related choices with `Checkbox.Group` so selections stay in sync. Pair the group with a leading Checkbox to provide a “Select all” control when the pattern fits the use case.
+
+
+
+### Disabled with tooltip
+
+Pair a disabled checkbox with a tooltip so users understand why the option is locked or how to gain access.
+
+
+
+### Stacked checkbox list
+
+Use individual Checkbox components when you need richer labels and supporting text. Keep the layout vertically aligned to make scanning easy.
+
+
+
+### Long consent copy
+
+Pair a checkbox with structured rich text for legal agreements. Highlight the primary sentence and keep supporting copy visually secondary.
+
+
diff --git a/docs/components/General/Button/Documentation.mdx b/docs/components/General/Button/Documentation.mdx
index 593c50e283..244cfd710f 100644
--- a/docs/components/General/Button/Documentation.mdx
+++ b/docs/components/General/Button/Documentation.mdx
@@ -1,4 +1,4 @@
-import { Meta, Canvas } from '@storybook/blocks'
+import { Meta, Canvas, Story } from '@storybook/blocks'
import * as ButtonStories from '../../../../src/components/general/Button/Button.stories'
@@ -8,82 +8,59 @@ import * as ButtonStories from '../../../../src/components/general/Button/Button
# Button
-#### Overview
+### Overview
The **Button** component is used to trigger actions or navigate the interface. It supports various styles, sizes, and states to fit different use cases.
-#### Key Features
+### Button Types
-- **Variants:** Includes primary, default, dashed, text, and link buttons for different use cases.
-- **Sizes:** Supports small, medium (default), and large sizes.
-- **States:** Disabled, loading, and hover states to indicate action availability.
-- **Icons:** Easily integrate icons for visual clarity and emphasis.
+- **Primary** – High-emphasis actions like submitting or saving workflows.
+- **Secondary** – Supportive tasks such as canceling or opening supplemental dialogs.
+- **Tertiary** – Lower-priority options that should sit behind primary/secondary CTAs.
+- **Text** – Quiet inline affordances that should blend into surrounding content.
+- **Link** – Navigation-style actions that resemble hyperlinks within layouts.
+
-### Button Variants and Use Cases
+
-#### Primary Button
+### Button sizes
-**Use Case**: For the main action on a page or section.
-- **Example:** Submitting a form, saving changes, or initiating a key process.
+Buttons ship in three heights to support different layouts. The default is `middle`, shown alongside the smaller and larger options below.
-**Examples**:
+
-- **With Icon**:
-
+### Buttons with icons
-- **Without Icon**:
-
+The icon-enabled variants mirror the same hierarchy while reinforcing meaning with glyphs.
-#### Default Button
+
-**Use Case**: For secondary actions that complement the primary task.
-- **Example:** Canceling an action, opening a dialog, or performing less important tasks.
+#### Icon-only buttons
-**Examples**:
+Icon-only buttons provide compact controls for toolbars and quick actions where text isn’t necessary.
-- **With Icon**:
-
-
-- **Without Icon**:
-
-
-#### Dashed Button
-
-**Use Case**: For tertiary actions in the visual hierarchy.
-
-**Examples**:
-
-- **With Icon**:
-
-
-- **Without Icon**:
-
+
-#### Link Button
+### Loading state
-**Use Case**: Ideal for triggering an actions that don't require heavy visual emphasis.
-- **Example:** Viewing errors, navigating to documentation
-When navigating to a new URL or external page, always use [Typography.Link](https://mparticle.github.io/aquarium/?path=/story/components-general-typography-link--primary) instead of a Link Button.
+The loading variant keeps the user informed while an action finishes processing. Use concise labels that set expectations.
-**Examples**:
+
-- **With Icon**:
-
+### Loading text button
-- **Without Icon**:
-
+Use the text treatment for subtle “load more” affordances that need to display progress without taking extra visual weight.
-#### Icon-Only Button
+
-**Use Case**: For compact controls where only an icon is needed.
-- **Example:** Toolbar controls, close buttons, or quick actions.
+### Destructive action
-**Examples**:
-
+Use the danger style to signal irreversible steps such as deletions. Pair it with clear language so users understand the impact.
+
-#### Related Links
+### Related Links
| Type | Resource |
| ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
diff --git a/docs/components/UX Patterns/StatisticsCard/Documentation.mdx b/docs/components/UX Patterns/StatisticsCard/Documentation.mdx
new file mode 100644
index 0000000000..79023390dd
--- /dev/null
+++ b/docs/components/UX Patterns/StatisticsCard/Documentation.mdx
@@ -0,0 +1,23 @@
+import { Meta, Canvas } from '@storybook/blocks'
+
+import StatisticsCardStories, { ModelMetrics } from '../../../../src/components/UXPatterns/StatisticsCard/StatisticsCard.stories'
+
+
+
+# Statistics Card
+
+A card component for displaying metric values with optional tooltips, commonly used for model metrics, performance indicators, or key statistics.
+
+## When to Use
+
+- Displaying model performance metrics (accuracy, precision, recall, AUC)
+- Showing key performance indicators (KPIs)
+- Presenting statistical data with contextual help
+- Creating metric dashboards or summary cards
+
+## Model Metrics Example
+
+A common pattern showing multiple metrics in a grid layout:
+
+
+
diff --git a/docs/components/UX Patterns/Steps/Documentation.mdx b/docs/components/UX Patterns/Steps/Documentation.mdx
new file mode 100644
index 0000000000..40a868b01c
--- /dev/null
+++ b/docs/components/UX Patterns/Steps/Documentation.mdx
@@ -0,0 +1,12 @@
+import { Meta, Canvas } from '@storybook/blocks'
+
+import StepsStories, { UXPatternExample } from '../../../../src/components/UXPatterns/Steps/Steps.stories'
+
+
+
+# Steps Progress Indicator
+
+Steps help communicate the current state of multistep flows. The following UX pattern centers the vertical label placement version inside its container so it feels like a dashboard callout.
+
+
+
diff --git a/src/components/POC/rokt-catalog/ExpandedSidebar.stories.tsx b/src/components/POC/rokt-catalog/ExpandedSidebar.stories.tsx
new file mode 100644
index 0000000000..9b1791461b
--- /dev/null
+++ b/src/components/POC/rokt-catalog/ExpandedSidebar.stories.tsx
@@ -0,0 +1,161 @@
+import { type Meta, type StoryObj } from '@storybook/react'
+import React, { useState } from 'react'
+import {
+ HomeOutlined,
+ SettingOutlined,
+ TagOutlined,
+ ShoppingCartOutlined,
+ ShopOutlined,
+ StarFilled,
+ AimOutlined,
+ FileTextOutlined,
+ InboxOutlined,
+ FolderOutlined,
+ SendOutlined,
+ LineChartOutlined,
+} from '@ant-design/icons'
+import { Avatar } from 'src/components'
+import { ColorBorder, MpBrandSecondary5 } from 'src/styles/style'
+import { ExpandedSidebar, type IExpandedSidebarItem, type IExpandedSidebarSection } from './ExpandedSidebar'
+
+// Simple gray avatar for additional partners
+const GrayAvatar = () => (
+
+)
+
+// Navigation items matching the Figma design
+const roktNavItems: IExpandedSidebarItem[] = [
+ { key: 'home', icon: , label: 'Home' },
+ { key: 'settings', icon: , label: 'Settings' },
+ { key: 'products', icon: , label: 'Products' },
+ { key: 'orders', icon: , label: 'Orders' },
+]
+
+// Featured partners (polished)
+const featuredPartners: IExpandedSidebarItem[] = [
+ {
+ key: 'nordstrom',
+ icon: (
+
+ N
+
+ ),
+ label: 'Nordstrom',
+ },
+ {
+ key: 'macys',
+ icon: (
+
+
+
+ ),
+ label: "Macy's",
+ },
+ {
+ key: 'target',
+ icon: (
+
+
+
+ ),
+ label: 'Target',
+ },
+]
+
+// Additional partners (simple gray avatars)
+const additionalPartners: IExpandedSidebarItem[] = [
+ { key: 'shopsimon', icon: , label: 'ShopSimon' },
+ { key: 'canal-demo', icon: , label: 'Canal Demo Store' },
+ { key: 'forex', icon: , label: 'Forex (Deactivated)' },
+ { key: 'fellow', icon: , label: 'Fellow (Deactivated)' },
+ { key: 'alyssa', icon: , label: 'Alyssa Milano (Deact...' },
+ { key: 'flip', icon: , label: 'Flip (Deactivated)' },
+]
+
+// Function to build marketplace items with dynamic partners
+const buildMarketplaceItems = (expanded: boolean, onToggle: () => void): IExpandedSidebarItem[] => [
+ {
+ key: 'partners',
+ icon: ,
+ label: 'Partners',
+ children: [
+ ...featuredPartners,
+ ...(expanded ? additionalPartners : []),
+ {
+ key: 'see-toggle',
+ label: expanded ? 'See less' : 'See more',
+ onClick: onToggle,
+ },
+ ],
+ },
+ { key: 'proposals', icon: , label: 'Proposals', badge: 10 },
+ { key: 'inbox', icon: , label: 'Inbox', badge: 10 },
+ {
+ key: 'reports',
+ icon: ,
+ label: 'Reports',
+ children: [
+ { key: 'analytics', label: 'Analytics' },
+ { key: 'payouts', label: 'Payouts' },
+ ],
+ },
+]
+
+// Ads section items
+const adsItems: IExpandedSidebarItem[] = [
+ { key: 'campaign-manager', icon: , label: 'Campaign manager' },
+ { key: 'ads-analytics', icon: , label: 'Analytics' },
+]
+
+// Function to build sections with dynamic marketplace items
+const buildSections = (expanded: boolean, onToggle: () => void): IExpandedSidebarSection[] => [
+ { label: 'MARKETPLACE', items: buildMarketplaceItems(expanded, onToggle) },
+ { label: 'ADS', items: adsItems },
+]
+
+const meta: Meta = {
+ title: 'POC/Rokt Catalog/ExpandedSidebar',
+ component: ExpandedSidebar,
+ parameters: {
+ layout: 'fullscreen',
+ },
+ decorators: [
+ Story => (
+
+
+ )
+}
diff --git a/src/components/UXPatterns/StatisticsCard/StatisticsCard.stories.tsx b/src/components/UXPatterns/StatisticsCard/StatisticsCard.stories.tsx
new file mode 100644
index 0000000000..dc9373d721
--- /dev/null
+++ b/src/components/UXPatterns/StatisticsCard/StatisticsCard.stories.tsx
@@ -0,0 +1,57 @@
+import { type Meta, type StoryObj } from '@storybook/react'
+import { StatisticsCard } from './StatisticsCard'
+import { Row, Col } from 'src/components'
+
+const meta: Meta = {
+ title: 'UX Patterns/StatisticsCard',
+ component: StatisticsCard,
+ parameters: {
+ layout: 'centered',
+ },
+}
+export default meta
+
+type Story = StoryObj
+
+export const ModelMetrics: Story = {
+ render: () => {
+ const metrics = [
+ {
+ title: 'Precision',
+ value: 72,
+ tooltip:
+ 'Precision focuses on how many of the users predicted to act actually do so. High precision means less wasted impressions or messaging.',
+ },
+ {
+ title: 'Recall',
+ value: 76,
+ tooltip:
+ 'Recall shows how many of the actual converters the model successfully identifies. Higher recall means fewer missed opportunities.',
+ },
+ {
+ title: 'Accuracy',
+ value: 87,
+ tooltip:
+ "Accuracy shows how often the model's predictions match what really happens, giving you confidence in your audience targeting or personalization strategy.",
+ },
+ {
+ title: 'AUC',
+ value: 87,
+ tooltip:
+ 'AUC tells you how powerful your model is at separating likely converters from everyone else — the higher the AUC, the better your targeting precision.',
+ },
+ ]
+
+ return (
+
+ {
+ setChecked(event.target.checked)
+ }}>
+
+ I agree to the terms, policies, and any other legal guidelines required to use this service, including matters
+ related to privacy, data usage, third-party tools, cookies, and future updates to the agreement.
+
+
+
+ )
+}
+
+const DisabledWithTooltipExample = () => (
+
+
+ Checkbox label
+
+
+)
+
/*
Initial story templates generated by AI.
Customize the stories based on specific requirements.
@@ -45,6 +184,69 @@ type Story = StoryObj
export const Primary: Story = {
args: {
disabled: false,
- children: 'Don’t show this message again',
+ children: 'Checkbox label',
+ },
+}
+
+export const StatesShowcase: Story = {
+ name: 'States showcase',
+ render: () => ,
+ parameters: {
+ docs: {
+ description: {
+ story: 'Display the default, checked, indeterminate, and disabled states together for fast visual comparison.',
+ },
+ },
+ },
+}
+
+export const DisabledWithTooltip: Story = {
+ name: 'Disabled with tooltip',
+ render: () => ,
+ parameters: {
+ docs: {
+ description: {
+ story: 'Wrap a disabled checkbox with a tooltip to explain why the option is unavailable.',
+ },
+ },
+ },
+}
+
+export const GroupSelection: Story = {
+ name: 'Group selection',
+ render: () => ,
+ parameters: {
+ docs: {
+ description: {
+ story:
+ 'Use `Checkbox.Group` to render related choices. Combine it with a single Checkbox to create a “Select all” control when needed.',
+ },
+ },
+ },
+}
+
+export const MultipleOptions: Story = {
+ name: 'Multiple options',
+ render: () => ,
+ parameters: {
+ docs: {
+ description: {
+ story:
+ 'Stack individual checkboxes to present detailed choices. Each checkbox can include supporting text to clarify what opting in entails.',
+ },
+ },
+ },
+}
+
+export const LongFormConsent: Story = {
+ name: 'Long form consent',
+ render: () => ,
+ parameters: {
+ docs: {
+ description: {
+ story:
+ 'Use a single Checkbox with stacked typography for lengthy consent copy. Keep supporting text in a secondary color to maintain readability.',
+ },
+ },
},
}
diff --git a/src/components/data-entry/Checkbox/Checkbox.tsx b/src/components/data-entry/Checkbox/Checkbox.tsx
index b1dd1ba91e..f7236efa68 100644
--- a/src/components/data-entry/Checkbox/Checkbox.tsx
+++ b/src/components/data-entry/Checkbox/Checkbox.tsx
@@ -1,11 +1,13 @@
import { Checkbox as AntCheckbox } from 'antd'
import { type CheckboxProps as AntCheckboxProps } from 'antd'
+import { type CheckboxGroupProps as AntCheckboxGroupProps } from 'antd/es/checkbox/Group'
import { ConfigProvider } from 'src/components'
import './checkbox.css'
export interface ICheckboxProps extends AntCheckboxProps {}
+export interface ICheckboxGroupProps extends AntCheckboxGroupProps {}
-export const Checkbox = (props: ICheckboxProps) => {
+const CheckboxComponent = (props: ICheckboxProps) => {
return (
@@ -13,4 +15,12 @@ export const Checkbox = (props: ICheckboxProps) => {
)
}
-Checkbox.Group = AntCheckbox.Group
+const CheckboxGroup = (props: ICheckboxGroupProps) => {
+ return (
+
+
+
+ )
+}
+
+export const Checkbox = Object.assign(CheckboxComponent, { Group: CheckboxGroup })
diff --git a/src/components/data-entry/Checkbox/checkbox.css b/src/components/data-entry/Checkbox/checkbox.css
index ed954e67a6..7cef200a72 100644
--- a/src/components/data-entry/Checkbox/checkbox.css
+++ b/src/components/data-entry/Checkbox/checkbox.css
@@ -1,4 +1,10 @@
.ant-checkbox-wrapper {
+ display: inline-flex;
+ align-items: flex-start;
font-weight: 400;
}
+.ant-checkbox {
+ align-self: flex-start;
+ margin-top: var(--margin-xxs);
+}
diff --git a/src/components/data-entry/DimensionPicker/DimensionPicker.stories.tsx b/src/components/data-entry/DimensionPicker/DimensionPicker.stories.tsx
new file mode 100644
index 0000000000..1d10c4ce91
--- /dev/null
+++ b/src/components/data-entry/DimensionPicker/DimensionPicker.stories.tsx
@@ -0,0 +1,110 @@
+import { type Meta, type StoryObj } from '@storybook/react'
+import { useState } from 'react'
+import { DimensionPicker, type IDimensionCategory, type IDimensionItem } from './DimensionPicker'
+
+const sampleCategories: IDimensionCategory[] = [
+ { key: 'campaign', label: 'Campaign', icon: 'flag' },
+ { key: 'creative', label: 'Creative', icon: 'edit' },
+ { key: 'geographic', label: 'Geographic', icon: 'placeholder' },
+ { key: 'layout', label: 'Layout', icon: 'grid' },
+ { key: 'page', label: 'Page', icon: 'openTab' },
+ { key: 'partner', label: 'Partner', icon: 'organization' },
+ { key: 'user', label: 'User', icon: 'user' },
+ { key: 'additional', label: 'Additional Dimensions', icon: 'add' },
+]
+
+const sampleItems: IDimensionItem[] = [
+ {
+ key: 'campaign-1',
+ label: 'Campaign ID',
+ categoryKey: 'campaign',
+ description: 'Unique identifier for the campaign.',
+ },
+ {
+ key: 'campaign-2',
+ label: 'Campaign Name',
+ categoryKey: 'campaign',
+ description: 'The display name of the campaign.',
+ },
+ {
+ key: 'campaign-3',
+ label: 'Campaign Status',
+ categoryKey: 'campaign',
+ description: 'Current status of the campaign (active, paused, completed).',
+ },
+ {
+ key: 'creative-1',
+ label: 'Creative ID',
+ categoryKey: 'creative',
+ description: 'Unique identifier for the creative asset.',
+ },
+ {
+ key: 'creative-2',
+ label: 'Creative Name',
+ categoryKey: 'creative',
+ description: 'The display name of the creative.',
+ },
+ { key: 'geo-1', label: 'Country', categoryKey: 'geographic', description: 'The country where the user is located.' },
+ { key: 'geo-2', label: 'Region', categoryKey: 'geographic', description: 'Geographic region or state.' },
+ { key: 'geo-3', label: 'City', categoryKey: 'geographic', description: 'City where the user is located.' },
+ {
+ key: 'layout-1',
+ label: 'Layout ID',
+ categoryKey: 'layout',
+ description: 'Unique identifier for the layout configuration.',
+ },
+ { key: 'layout-2', label: 'Layout Name', categoryKey: 'layout', description: 'Display name of the layout.' },
+ ...Array.from({ length: 15 }, (_, i) => ({
+ key: `page-${i + 1}`,
+ label: i === 0 ? 'Dimension menu item lorem' : 'Lorem dimension menu item',
+ categoryKey: 'page',
+ description:
+ 'Mauris enim cursus tristique et consequat ultricies amet luctus. Interdum elementum amet nunc suspendisse nam malesuada augue.',
+ })),
+ { key: 'partner-1', label: 'Partner ID', categoryKey: 'partner', description: 'Unique identifier for the partner.' },
+ {
+ key: 'partner-2',
+ label: 'Partner Name',
+ categoryKey: 'partner',
+ description: 'Display name of the partner organization.',
+ },
+ { key: 'user-1', label: 'User ID', categoryKey: 'user', description: 'Unique identifier for the user.' },
+ { key: 'user-2', label: 'User Segment', categoryKey: 'user', description: 'Audience segment the user belongs to.' },
+ {
+ key: 'add-1',
+ label: 'Custom Dimension 1',
+ categoryKey: 'additional',
+ description: 'Custom dimension defined by the user.',
+ },
+]
+
+const meta: Meta = {
+ title: 'POC/DimensionPicker',
+ component: DimensionPicker,
+ parameters: {
+ layout: 'centered',
+ },
+ args: {
+ categories: sampleCategories,
+ items: sampleItems,
+ },
+}
+
+export default meta
+
+type Story = StoryObj
+
+export const Primary: Story = {
+ render: () => {
+ const [selected, setSelected] = useState(['campaign-1'])
+ return (
+ console.log('Applied:', keys)}
+ />
+ )
+ },
+}
diff --git a/src/components/data-entry/DimensionPicker/DimensionPicker.tsx b/src/components/data-entry/DimensionPicker/DimensionPicker.tsx
new file mode 100644
index 0000000000..18bdf99235
--- /dev/null
+++ b/src/components/data-entry/DimensionPicker/DimensionPicker.tsx
@@ -0,0 +1,237 @@
+import { useState, useMemo, type ReactNode } from 'react'
+import { ConfigProvider, Input, Checkbox, Button, Empty, Typography } from 'src/components'
+import { Icon } from 'src/components/general/Icon/Icon'
+import type { IconNames } from 'src/types/icons'
+import './dimension-picker.css'
+
+export interface IDimensionCategory {
+ /** Unique identifier for the category */
+ key: string
+ /** Display label for the category */
+ label: string
+ /** Icon name from the icon library */
+ icon?: IconNames
+}
+
+export interface IDimensionItem {
+ /** Unique identifier for the dimension item */
+ key: string
+ /** Display label for the dimension */
+ label: string
+ /** Category key this dimension belongs to */
+ categoryKey: string
+ /** Optional description shown in the details panel */
+ description?: string
+ /** Whether this item is disabled */
+ disabled?: boolean
+}
+
+export interface IDimensionPickerProps {
+ /** Title shown above the category list */
+ categoryTitle?: string
+ /** Title shown above the description panel */
+ descriptionTitle?: string
+ /** List of available categories */
+ categories: IDimensionCategory[]
+ /** List of all dimension items */
+ items: IDimensionItem[]
+ /** Currently selected dimension keys */
+ value?: string[]
+ /** Default selected dimension keys (uncontrolled mode) */
+ defaultValue?: string[]
+ /** Callback when selection changes */
+ onChange?: (selectedKeys: string[]) => void
+ /** Callback when Apply button is clicked */
+ onApply?: (selectedKeys: string[]) => void
+ /** Callback when Clear all button is clicked */
+ onClearAll?: () => void
+ /** Placeholder text for the search input */
+ searchPlaceholder?: string
+ /** Text for the Clear all button */
+ clearAllText?: string
+ /** Text for the Apply button (count will be appended) */
+ applyText?: string
+ /** Whether to show the Apply button */
+ showApplyButton?: boolean
+ /** Whether to show the Clear all button */
+ showClearAllButton?: boolean
+ /** Whether to show the description panel */
+ showDescriptionPanel?: boolean
+ /** Custom empty state when no items match search */
+ emptyContent?: ReactNode
+ /** Custom className for the container */
+ className?: string
+ /** Loading state */
+ loading?: boolean
+}
+
+export const DimensionPicker = ({
+ categoryTitle = 'Dimensions categories',
+ descriptionTitle = 'Description',
+ categories,
+ items,
+ value,
+ defaultValue = [],
+ onChange,
+ onApply,
+ onClearAll,
+ searchPlaceholder = 'Search',
+ clearAllText = 'Clear all',
+ applyText = 'Apply',
+ showApplyButton = true,
+ showClearAllButton = true,
+ showDescriptionPanel = true,
+ emptyContent,
+ className = '',
+ loading = false,
+}: IDimensionPickerProps) => {
+ const [searchQuery, setSearchQuery] = useState('')
+ const [selectedCategory, setSelectedCategory] = useState(categories[0]?.key ?? '')
+ const [hoveredItem, setHoveredItem] = useState(null)
+ const [internalSelected, setInternalSelected] = useState(defaultValue)
+
+ // Controlled vs uncontrolled
+ const selectedKeys = value ?? internalSelected
+ const setSelectedKeys = (keys: string[]) => {
+ if (value === undefined) {
+ setInternalSelected(keys)
+ }
+ onChange?.(keys)
+ }
+
+ // Filter items by category and search
+ const filteredItems = useMemo(() => {
+ let filtered = items.filter(item => item.categoryKey === selectedCategory)
+
+ if (searchQuery) {
+ const query = searchQuery.toLowerCase()
+ filtered = filtered.filter(item => item.label.toLowerCase().includes(query))
+ }
+
+ return filtered
+ }, [items, selectedCategory, searchQuery])
+
+ // Handle item selection
+ const handleItemToggle = (itemKey: string) => {
+ const newSelected = selectedKeys.includes(itemKey)
+ ? selectedKeys.filter(key => key !== itemKey)
+ : [...selectedKeys, itemKey]
+ setSelectedKeys(newSelected)
+ }
+
+ // Handle clear all
+ const handleClearAll = () => {
+ setSelectedKeys([])
+ onClearAll?.()
+ }
+
+ // Handle apply
+ const handleApply = () => {
+ onApply?.(selectedKeys)
+ }
+
+ const selectedCount = selectedKeys.length
+
+ return (
+
+