) => (
+
+ {patchChildren(
+
+ {children}
+ ,
+ (childProps: { className: string | string[] }) => ({
+ className: appendClassName(
+ childProps.className,
+ 'rcx-sidebar-v2-item__menu'
+ ),
+ })
+ )}
+
+);
diff --git a/packages/fuselage/src/components/SidebarV2/SidebarItem/SidebarItemRow.tsx b/packages/fuselage/src/components/SidebarV2/SidebarItem/SidebarItemRow.tsx
new file mode 100644
index 0000000000..aea9720d66
--- /dev/null
+++ b/packages/fuselage/src/components/SidebarV2/SidebarItem/SidebarItemRow.tsx
@@ -0,0 +1,13 @@
+import type { HTMLAttributes } from 'react';
+
+export const SidebarItemRow = ({
+ className,
+ ...props
+}: HTMLAttributes) => (
+
+);
diff --git a/packages/fuselage/src/components/SidebarV2/SidebarItem/SidebarItemStatusBullet.tsx b/packages/fuselage/src/components/SidebarV2/SidebarItem/SidebarItemStatusBullet.tsx
new file mode 100644
index 0000000000..7452cf6a60
--- /dev/null
+++ b/packages/fuselage/src/components/SidebarV2/SidebarItem/SidebarItemStatusBullet.tsx
@@ -0,0 +1,11 @@
+import type { ComponentProps } from 'react';
+
+import { StatusBullet } from '../../StatusBullet';
+
+export const SidebarItemStatusBullet = (
+ props: ComponentProps
+) => (
+
+
+
+);
diff --git a/packages/fuselage/src/components/SidebarV2/SidebarItem/SidebarItemTimestamp.tsx b/packages/fuselage/src/components/SidebarV2/SidebarItem/SidebarItemTimestamp.tsx
new file mode 100644
index 0000000000..af4a228884
--- /dev/null
+++ b/packages/fuselage/src/components/SidebarV2/SidebarItem/SidebarItemTimestamp.tsx
@@ -0,0 +1,16 @@
+import type { HTMLAttributes } from 'react';
+
+export const SidebarItemTimestamp = ({
+ className,
+ ...props
+}: HTMLAttributes) => (
+
+);
diff --git a/packages/fuselage/src/components/SidebarV2/SidebarItem/SidebarItemTitle.tsx b/packages/fuselage/src/components/SidebarV2/SidebarItem/SidebarItemTitle.tsx
new file mode 100644
index 0000000000..dc69989c2b
--- /dev/null
+++ b/packages/fuselage/src/components/SidebarV2/SidebarItem/SidebarItemTitle.tsx
@@ -0,0 +1,18 @@
+import type { HTMLAttributes } from 'react';
+
+export const SidebarItemTitle = ({
+ className,
+ unread,
+ ...props
+}: { unread?: boolean } & HTMLAttributes) => (
+
+);
diff --git a/packages/fuselage/src/components/SidebarV2/SidebarItem/SidebarListItem.tsx b/packages/fuselage/src/components/SidebarV2/SidebarItem/SidebarListItem.tsx
new file mode 100644
index 0000000000..b75957e825
--- /dev/null
+++ b/packages/fuselage/src/components/SidebarV2/SidebarItem/SidebarListItem.tsx
@@ -0,0 +1,24 @@
+import type { HTMLAttributes } from 'react';
+import { forwardRef } from 'react';
+
+type SidebarListItemProps = {
+ selected?: boolean;
+} & HTMLAttributes;
+
+export const SidebarListItem = forwardRef(
+ ({ className, children, ...props }, ref) => (
+
+ {children}
+
+ )
+);
diff --git a/packages/fuselage/src/components/SidebarV2/SidebarItem/index.ts b/packages/fuselage/src/components/SidebarV2/SidebarItem/index.ts
new file mode 100644
index 0000000000..632f198f25
--- /dev/null
+++ b/packages/fuselage/src/components/SidebarV2/SidebarItem/index.ts
@@ -0,0 +1,13 @@
+export { SidebarItem as SidebarV2Item } from './SidebarItem';
+export { SidebarItemAction as SidebarV2ItemAction } from './SidebarItemAction';
+export { SidebarItemAvatarWrapper as SidebarV2ItemAvatarWrapper } from './SidebarItemAvatarWrapper';
+export { SidebarItemBadge as SidebarV2ItemBadge } from './SidebarItemBadge';
+export { SidebarItemIcon as SidebarV2ItemIcon } from './SidebarItemIcon';
+export { SidebarItemMenu as SidebarV2ItemMenu } from './SidebarItemMenu';
+export { SidebarItemTitle as SidebarV2ItemTitle } from './SidebarItemTitle';
+export { SidebarItemRow as SidebarV2ItemRow } from './SidebarItemRow';
+export { SidebarItemCol as SidebarV2ItemCol } from './SidebarItemCol';
+export { SidebarItemTimestamp as SidebarV2ItemTimestamp } from './SidebarItemTimestamp';
+export { SidebarListItem as SidebarV2ListItem } from './SidebarListItem';
+export { SidebarItemContent as SidebarV2ItemContent } from './SidebarItemContent';
+export { SidebarItemStatusBullet as SidebarV2ItemStatusBullet } from './SidebarItemStatusBullet';
diff --git a/packages/fuselage/src/components/SidebarV2/SidebarLink.tsx b/packages/fuselage/src/components/SidebarV2/SidebarLink.tsx
new file mode 100644
index 0000000000..c147dfc805
--- /dev/null
+++ b/packages/fuselage/src/components/SidebarV2/SidebarLink.tsx
@@ -0,0 +1,52 @@
+import type { Keys as Icons } from '@rocket.chat/icons';
+import type { LinkHTMLAttributes, ReactNode } from 'react';
+
+import { appendClassName } from '../../helpers/appendClassName';
+import { patchChildren } from '../../helpers/patchChildren';
+import { Icon } from '../Icon';
+
+export const SidebarLink = ({
+ selected,
+ icon,
+ badge,
+ menu,
+ ...props
+}: {
+ selected?: boolean;
+ icon?: Icons;
+ badge?: ReactNode;
+ menu?: ReactNode;
+} & LinkHTMLAttributes) => (
+ e.stopPropagation()}
+ {...props}
+ >
+ {icon && (
+
+ )}
+
+ {props.children}
+
+ {badge}
+ {menu &&
+ patchChildren(
+
+ {menu}
+ ,
+ (childProps: { className: string | string[] }) => ({
+ className: appendClassName(
+ childProps.className,
+ 'rcx-box rcx-box--full rcx-sidebar-v2-item__menu rcx-box--animated'
+ ),
+ })
+ )}
+
+);
diff --git a/packages/fuselage/src/components/SidebarV2/SidebarMedia/SidebarMedia.stories.tsx b/packages/fuselage/src/components/SidebarV2/SidebarMedia/SidebarMedia.stories.tsx
new file mode 100644
index 0000000000..1984e42af7
--- /dev/null
+++ b/packages/fuselage/src/components/SidebarV2/SidebarMedia/SidebarMedia.stories.tsx
@@ -0,0 +1,32 @@
+import type { StoryFn, Meta } from '@storybook/react';
+
+import {
+ SidebarV2 as Sidebar,
+ SidebarV2Media as SidebarMedia,
+ SidebarV2MediaTitle as SidebarMediaTitle,
+ SidebarV2MediaController as SidebarMediaController,
+ Box,
+ IconButton,
+} from '../..';
+import { GenericCallItem } from '../helpers';
+
+export default {
+ title: 'Navigation/SidebarV2/Media',
+ component: Sidebar,
+} satisfies Meta;
+
+export const Default: StoryFn = () => (
+
+
+
+ 3 calls in queue
+
+
+
+
+
+
+
+
+
+);
diff --git a/packages/fuselage/src/components/SidebarV2/SidebarMedia/SidebarMedia.styles.scss b/packages/fuselage/src/components/SidebarV2/SidebarMedia/SidebarMedia.styles.scss
new file mode 100644
index 0000000000..49521890e4
--- /dev/null
+++ b/packages/fuselage/src/components/SidebarV2/SidebarMedia/SidebarMedia.styles.scss
@@ -0,0 +1,42 @@
+@use '../../../styles/colors.scss';
+@use '../../../styles/lengths.scss';
+@use '../../../styles/typography.scss';
+@import '../../../styles/mixins/all.scss';
+
+$sidebar-media-color: theme('sidebar-media-color', colors.font(titles-labels));
+
+$sidebar-media-border-color: theme(
+ 'sidebar-media-border-color',
+ colors.stroke(light)
+);
+
+.rcx-sidebar-v2-media {
+ padding-block: lengths.padding(8);
+
+ color: $sidebar-media-color;
+ border-bottom: lengths.border-width(default) solid $sidebar-media-border-color;
+ background-color: $sidebar-color-surface-default;
+
+ &__title {
+ @include typography.use-font-scale(c1);
+ flex-grow: 1;
+
+ padding-block-end: lengths.padding(4);
+
+ padding-inline: lengths.padding(16);
+
+ text-align: center;
+ }
+
+ &__controller {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ padding-inline: lengths.padding(16);
+
+ &__label {
+ @include typography.use-font-scale(p2);
+ }
+ }
+}
diff --git a/packages/fuselage/src/components/SidebarV2/SidebarMedia/SidebarMedia.tsx b/packages/fuselage/src/components/SidebarV2/SidebarMedia/SidebarMedia.tsx
new file mode 100644
index 0000000000..615a42b1bb
--- /dev/null
+++ b/packages/fuselage/src/components/SidebarV2/SidebarMedia/SidebarMedia.tsx
@@ -0,0 +1,11 @@
+import type { HTMLAttributes } from 'react';
+
+export const SidebarMedia = ({
+ className,
+ ...props
+}: HTMLAttributes) => (
+
+);
diff --git a/packages/fuselage/src/components/SidebarV2/SidebarMedia/SidebarMediaController.tsx b/packages/fuselage/src/components/SidebarV2/SidebarMedia/SidebarMediaController.tsx
new file mode 100644
index 0000000000..244f9b4c1b
--- /dev/null
+++ b/packages/fuselage/src/components/SidebarV2/SidebarMedia/SidebarMediaController.tsx
@@ -0,0 +1,21 @@
+import type { HTMLAttributes } from 'react';
+
+import { SidebarButtonGroup } from '../SidebarButtonGroup';
+
+export const SidebarMediaController = ({
+ className,
+ label,
+ children,
+ ...props
+}: { label?: string } & HTMLAttributes) => (
+
+
{label}
+
+ {children}
+
+
+);
diff --git a/packages/fuselage/src/components/SidebarV2/SidebarMedia/SidebarMediaTitle.tsx b/packages/fuselage/src/components/SidebarV2/SidebarMedia/SidebarMediaTitle.tsx
new file mode 100644
index 0000000000..adc9b0532e
--- /dev/null
+++ b/packages/fuselage/src/components/SidebarV2/SidebarMedia/SidebarMediaTitle.tsx
@@ -0,0 +1,13 @@
+import type { HTMLAttributes } from 'react';
+
+export const SidebarMediaTitle = ({
+ className,
+ ...props
+}: HTMLAttributes) => (
+
+);
diff --git a/packages/fuselage/src/components/SidebarV2/SidebarMedia/index.ts b/packages/fuselage/src/components/SidebarV2/SidebarMedia/index.ts
new file mode 100644
index 0000000000..3f5ae5e073
--- /dev/null
+++ b/packages/fuselage/src/components/SidebarV2/SidebarMedia/index.ts
@@ -0,0 +1,3 @@
+export { SidebarMedia as SidebarV2Media } from './SidebarMedia';
+export { SidebarMediaController as SidebarV2MediaController } from './SidebarMediaController';
+export { SidebarMediaTitle as SidebarV2MediaTitle } from './SidebarMediaTitle';
diff --git a/packages/fuselage/src/components/SidebarV2/SidebarSection.tsx b/packages/fuselage/src/components/SidebarV2/SidebarSection.tsx
new file mode 100644
index 0000000000..8fd4709ad9
--- /dev/null
+++ b/packages/fuselage/src/components/SidebarV2/SidebarSection.tsx
@@ -0,0 +1,10 @@
+import type { HTMLAttributes } from 'react';
+
+import { SidebarDivider } from './SidebarDivider';
+
+export const SidebarSection = (props: HTMLAttributes) => (
+
+);
diff --git a/packages/fuselage/src/components/SidebarV2/helpers.tsx b/packages/fuselage/src/components/SidebarV2/helpers.tsx
new file mode 100644
index 0000000000..da1643a36a
--- /dev/null
+++ b/packages/fuselage/src/components/SidebarV2/helpers.tsx
@@ -0,0 +1,206 @@
+import type { Decorator } from '@storybook/react';
+import type { ComponentProps, ReactElement } from 'react';
+
+import {
+ SidebarV2,
+ SidebarV2Accordion,
+ SidebarV2AccordionItem,
+ SidebarV2Actions,
+ SidebarV2ButtonGroup,
+ SidebarV2CollapseGroup,
+ SidebarV2Footer,
+ SidebarV2FooterContent,
+ SidebarV2Item,
+ SidebarV2ItemAvatarWrapper,
+ SidebarV2ItemBadge,
+ SidebarV2ItemCol,
+ SidebarV2ItemContent,
+ SidebarV2ItemIcon,
+ SidebarV2ItemMenu,
+ SidebarV2ItemRow,
+ SidebarV2ItemStatusBullet,
+ SidebarV2ItemTimestamp,
+ SidebarV2ItemTitle,
+ SidebarV2ListItem,
+} from '.';
+import { Avatar, Box, IconButton, MenuV2 as Menu, MenuItem } from '../..';
+
+export const leterAvatarUrls = [
+ "data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3e%3crect width='100%25' height='100%25' fill='%2310529E'/%3e%3ctext x='50%25' y='50%25' dy='0.36em' text-anchor='middle' pointer-events='none' fill='white' font-size='125' font-family='Helvetica%2c sans-serif'%3eB%3c/text%3e%3c/svg%3e",
+ "data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3e%3crect width='100%25' height='100%25' fill='%23158D65'/%3e%3ctext x='50%25' y='50%25' dy='0.36em' text-anchor='middle' pointer-events='none' fill='white' font-size='125' font-family='Helvetica%2c sans-serif'%3eG%3c/text%3e%3c/svg%3e",
+ "data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3e%3crect width='100%25' height='100%25' fill='%237F1B9F'/%3e%3ctext x='50%25' y='50%25' dy='0.36em' text-anchor='middle' pointer-events='none' fill='white' font-size='125' font-family='Helvetica%2c sans-serif'%3eP%3c/text%3e%3c/svg%3e",
+ "data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3e%3crect width='100%25' height='100%25' fill='%23B68D00'/%3e%3ctext x='50%25' y='50%25' dy='0.36em' text-anchor='middle' pointer-events='none' fill='white' font-size='125' font-family='Helvetica%2c sans-serif'%3eY%3c/text%3e%3c/svg%3e",
+];
+
+export const names = [
+ 'Marvin Black',
+ 'Esther Miles',
+ 'Leroy Jenkins',
+ 'Morgan Freeman looooooooooong looooooooooong looooooooooong name',
+ 'John Doe',
+ 'Jane Doe',
+ 'Baz Qux',
+ 'Alice Bob',
+ 'Charlie Delta',
+ 'Eve Mallory',
+];
+
+export const MenuTemplate = () => (
+
+);
+
+export const GenericCondensedItem = ({ i = 0 }: { i: number }) => (
+
+
+
+
+
+
+
+ {names[i % 10]}
+
+ {i % 2 !== 0 && (
+
+ )}
+ {i === 0 && (
+
+
+
+
+ )}
+ } />
+
+
+);
+
+export const GenericNoAvatarItem = ({ i = 0 }: { i: number }) => (
+
+
+
+ {names[i % 10]}
+
+ } />
+
+
+);
+
+export const GenericMediumItem = ({ i = 0 }: { i: number }) => (
+
+
+
+
+
+
+ {names[i % 10]}
+
+ } />
+
+
+);
+
+export const GenericExtendedItem = ({ i = 0 }: { i: number }) => (
+
+
+
+
+
+
+
+
+
+ {names[i % 10]}
+ 12:00
+
+
+
+ No messages yet
+
+ } />
+
+
+
+
+);
+
+export const GenericCallItem = ({
+ i = 0,
+ ...props
+}: { i?: number } & ComponentProps) => (
+
+
+
+
+
+
+ {names[i % 10]}
+ Calling
+
+
+
+
+
+
+
+
+
+);
+
+export const decorators: Decorator[] = [
+ (fn): ReactElement => (
+
+
+
+ }
+ >
+ }
+ >
+ {fn()}
+
+
+
+
+
+ Powered by Rocket.Chat
+
+
+ Free edition
+
+
+
+
+ ),
+];
diff --git a/packages/fuselage/src/components/SidebarV2/hooks/useCollapse.tsx b/packages/fuselage/src/components/SidebarV2/hooks/useCollapse.tsx
new file mode 100644
index 0000000000..81758a91d5
--- /dev/null
+++ b/packages/fuselage/src/components/SidebarV2/hooks/useCollapse.tsx
@@ -0,0 +1,66 @@
+import { useToggle, useUniqueId } from '@rocket.chat/fuselage-hooks';
+import type { KeyboardEventHandler, MouseEvent } from 'react';
+
+export const useCollapse = ({
+ expanded: propExpanded,
+ defaultExpanded,
+ disabled,
+ noncollapsible,
+ tabIndex = 0,
+}: {
+ expanded?: boolean;
+ defaultExpanded?: boolean;
+ disabled?: boolean;
+ noncollapsible?: boolean;
+ tabIndex?: number;
+}) => {
+ const [stateExpanded, toggleStateExpanded] = useToggle(defaultExpanded);
+ const expanded = propExpanded || stateExpanded;
+
+ const panelExpanded = noncollapsible || expanded;
+
+ const titleId = useUniqueId();
+ const panelId = useUniqueId();
+
+ const handleClick = (e: MouseEvent) => {
+ if (disabled) {
+ return;
+ }
+ e.currentTarget?.blur();
+ toggleStateExpanded();
+ };
+
+ const handleKeyDown: KeyboardEventHandler = (event) => {
+ if (disabled || event.currentTarget !== event.target) {
+ return;
+ }
+
+ if (['Enter', 'Space'].includes(event.code)) {
+ event.preventDefault();
+
+ if (event.repeat) {
+ return;
+ }
+
+ toggleStateExpanded();
+ }
+ };
+
+ const collapsibleProps = {
+ 'aria-controls': panelId,
+ 'aria-expanded': expanded ? 'true' : 'false',
+ 'tabIndex': !disabled ? tabIndex : undefined,
+ 'onClick': handleClick,
+ 'onKeyDown': handleKeyDown,
+ } as const;
+
+ const nonCollapsibleProps = {
+ 'aria-disabled': 'true',
+ 'aria-expanded': 'true',
+ 'aria-labelledby': titleId,
+ } as const;
+
+ const barProps = noncollapsible ? nonCollapsibleProps : collapsibleProps;
+
+ return { barProps, titleId, panelId, panelExpanded, expanded };
+};
diff --git a/packages/fuselage/src/components/SidebarV2/index.tsx b/packages/fuselage/src/components/SidebarV2/index.tsx
new file mode 100644
index 0000000000..38fd316df7
--- /dev/null
+++ b/packages/fuselage/src/components/SidebarV2/index.tsx
@@ -0,0 +1,15 @@
+export { Sidebar as SidebarV2 } from './Sidebar';
+export { SidebarAccordion as SidebarV2Accordion } from './SidebarAccordion';
+export { SidebarAccordionItem as SidebarV2AccordionItem } from './SidebarAccordionItem';
+export { SidebarAction as SidebarV2Action } from './SidebarAction';
+export { SidebarActions as SidebarV2Actions } from './SidebarActions';
+export { SidebarLink as SidebarV2Link } from './SidebarLink';
+export * from './SidebarItem';
+export { SidebarCollapseGroup as SidebarV2CollapseGroup } from './SidebarCollapseGroup';
+export { SidebarBanner as SidebarV2Banner } from './SidebarBanner';
+export * from './SidebarFooter';
+export * from './SidebarMedia';
+export { SidebarButtonGroup as SidebarV2ButtonGroup } from './SidebarButtonGroup';
+export { SidebarSection as SidebarV2Section } from './SidebarSection';
+export { SidebarDivider as SidebarV2Divider } from './SidebarDivider';
+export { SidebarGroupTitle as SidebarV2GroupTitle } from './SidebarGroupTitle';
diff --git a/packages/fuselage/src/components/Sidepanel/Sidepanel.spec.tsx b/packages/fuselage/src/components/Sidepanel/Sidepanel.spec.tsx
new file mode 100644
index 0000000000..13f26cdaee
--- /dev/null
+++ b/packages/fuselage/src/components/Sidepanel/Sidepanel.spec.tsx
@@ -0,0 +1,19 @@
+import { composeStories } from '@storybook/react';
+import { axe } from 'jest-axe';
+
+import { render } from '../../testing';
+import * as stories from './Sidepanel.stories';
+
+const { Default } = composeStories(stories);
+
+describe('[Sidebar Default story]', () => {
+ it('renders without crashing', () => {
+ render();
+ });
+ it('should have no a11y violations', async () => {
+ const { container } = render();
+
+ const results = await axe(container);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/fuselage/src/components/Sidepanel/Sidepanel.stories.tsx b/packages/fuselage/src/components/Sidepanel/Sidepanel.stories.tsx
new file mode 100644
index 0000000000..4790068305
--- /dev/null
+++ b/packages/fuselage/src/components/Sidepanel/Sidepanel.stories.tsx
@@ -0,0 +1,75 @@
+import type { Meta, StoryFn } from '@storybook/react';
+
+import {
+ Sidepanel,
+ SidepanelSection,
+ SidepanelHeader,
+ SidepanelHeaderTitle,
+ SidepanelList,
+ SidepanelListItem,
+} from '.';
+import {
+ Avatar,
+ Box,
+ Icon,
+ IconButton,
+ InputBox,
+ SidebarItem,
+ SidebarItemBadge,
+ SidebarItemIcon,
+ SidebarItemMenu,
+ SidebarItemTitle,
+} from '../..';
+import { SidebarItemAvatarWrapper } from '../SidebarV2/SidebarItem/SidebarItemAvatarWrapper';
+import { MenuTemplate, leterAvatarUrls, names } from '../SidebarV2/helpers';
+
+export default {
+ title: 'Navigation/Sidepanel',
+ component: Sidepanel,
+ decorators: [
+ (Story) => (
+
+
+
+ ),
+ ],
+} as Meta;
+
+const Template: StoryFn = (args) => (
+
+
+ All
+
+
+
+ }
+ />
+
+
+
+ {new Array(20).fill(null).map((_, index) => (
+
+
+
+
+
+
+ {names[index % 10]}
+
+ } />
+
+
+ ))}
+
+
+
+);
+
+export const Default = Template.bind({});
diff --git a/packages/fuselage/src/components/Sidepanel/Sidepanel.styles.scss b/packages/fuselage/src/components/Sidepanel/Sidepanel.styles.scss
new file mode 100644
index 0000000000..3274e0effa
--- /dev/null
+++ b/packages/fuselage/src/components/Sidepanel/Sidepanel.styles.scss
@@ -0,0 +1,44 @@
+@use '../../styles/colors.scss';
+@use '../../styles/lengths.scss';
+@use '../../styles/typography.scss';
+
+$sidepanel-color-default: colors.font(default);
+
+%section-base {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: lengths.padding(8);
+}
+
+.rcx-sidepanel {
+ display: flex;
+ flex-direction: column;
+ flex: 1 0 auto;
+
+ width: lengths.size(276);
+
+ color: $sidepanel-color-default;
+ border-right: lengths.border-width(default) solid colors.stroke(extra-light);
+
+ background-color: colors.surface(room);
+
+ &-section {
+ @extend %section-base;
+ padding: lengths.padding(16);
+ }
+
+ &-header {
+ @extend %section-base;
+ height: lengths.size(44);
+ padding-inline: lengths.padding(16);
+
+ &__title {
+ @include typography.use-font-scale(h4);
+ }
+ }
+
+ &-list {
+ padding-block: lengths.padding(8);
+ }
+}
diff --git a/packages/fuselage/src/components/Sidepanel/Sidepanel.tsx b/packages/fuselage/src/components/Sidepanel/Sidepanel.tsx
new file mode 100644
index 0000000000..50366385fb
--- /dev/null
+++ b/packages/fuselage/src/components/Sidepanel/Sidepanel.tsx
@@ -0,0 +1,11 @@
+import type { HTMLAttributes } from 'react';
+
+export const Sidepanel = ({
+ className,
+ ...props
+}: HTMLAttributes) => (
+
+);
diff --git a/packages/fuselage/src/components/Sidepanel/SidepanelDivider.tsx b/packages/fuselage/src/components/Sidepanel/SidepanelDivider.tsx
new file mode 100644
index 0000000000..7fa6d4f506
--- /dev/null
+++ b/packages/fuselage/src/components/Sidepanel/SidepanelDivider.tsx
@@ -0,0 +1,5 @@
+import { Divider } from '../Divider';
+
+export const SidepanelDivider = () => (
+
+);
diff --git a/packages/fuselage/src/components/Sidepanel/SidepanelHeader.tsx b/packages/fuselage/src/components/Sidepanel/SidepanelHeader.tsx
new file mode 100644
index 0000000000..947750bd51
--- /dev/null
+++ b/packages/fuselage/src/components/Sidepanel/SidepanelHeader.tsx
@@ -0,0 +1,16 @@
+import type { HTMLAttributes } from 'react';
+
+import { SidepanelDivider } from './SidepanelDivider';
+
+export const SidepanelHeader = ({
+ className,
+ ...props
+}: HTMLAttributes) => (
+ <>
+
+
+ >
+);
diff --git a/packages/fuselage/src/components/Sidepanel/SidepanelHeaderTitle.tsx b/packages/fuselage/src/components/Sidepanel/SidepanelHeaderTitle.tsx
new file mode 100644
index 0000000000..dd1bdc78c7
--- /dev/null
+++ b/packages/fuselage/src/components/Sidepanel/SidepanelHeaderTitle.tsx
@@ -0,0 +1,13 @@
+import type { HTMLAttributes } from 'react';
+
+export const SidepanelHeaderTitle = ({
+ className,
+ ...props
+}: HTMLAttributes) => (
+
+);
diff --git a/packages/fuselage/src/components/Sidepanel/SidepanelList.tsx b/packages/fuselage/src/components/Sidepanel/SidepanelList.tsx
new file mode 100644
index 0000000000..5393665796
--- /dev/null
+++ b/packages/fuselage/src/components/Sidepanel/SidepanelList.tsx
@@ -0,0 +1,16 @@
+import type { HTMLAttributes } from 'react';
+import { forwardRef } from 'react';
+
+export const SidepanelList = forwardRef<
+ HTMLDivElement,
+ HTMLAttributes
+>(function SidepanelList({ className, ...props }, ref) {
+ return (
+
+ );
+});
diff --git a/packages/fuselage/src/components/Sidepanel/SidepanelListItem.tsx b/packages/fuselage/src/components/Sidepanel/SidepanelListItem.tsx
new file mode 100644
index 0000000000..5a43f39325
--- /dev/null
+++ b/packages/fuselage/src/components/Sidepanel/SidepanelListItem.tsx
@@ -0,0 +1,18 @@
+import type { HTMLAttributes } from 'react';
+import { forwardRef } from 'react';
+
+export const SidepanelListItem = forwardRef<
+ HTMLDivElement,
+ HTMLAttributes
+>(function SidepanelListItem({ className, ...props }, ref) {
+ return (
+
+ );
+});
diff --git a/packages/fuselage/src/components/Sidepanel/SidepanelSection.tsx b/packages/fuselage/src/components/Sidepanel/SidepanelSection.tsx
new file mode 100644
index 0000000000..c1813891a2
--- /dev/null
+++ b/packages/fuselage/src/components/Sidepanel/SidepanelSection.tsx
@@ -0,0 +1,11 @@
+import type { HTMLAttributes } from 'react';
+
+export const SidepanelSection = ({
+ className,
+ ...props
+}: HTMLAttributes) => (
+
+);
diff --git a/packages/fuselage/src/components/Sidepanel/SidepanelSectionAction.tsx b/packages/fuselage/src/components/Sidepanel/SidepanelSectionAction.tsx
new file mode 100644
index 0000000000..59fe628543
--- /dev/null
+++ b/packages/fuselage/src/components/Sidepanel/SidepanelSectionAction.tsx
@@ -0,0 +1,13 @@
+import type { HTMLAttributes } from 'react';
+
+export const SidepanelSectionAction = ({
+ className,
+ ...props
+}: HTMLAttributes) => (
+
+);
diff --git a/packages/fuselage/src/components/Sidepanel/index.ts b/packages/fuselage/src/components/Sidepanel/index.ts
new file mode 100644
index 0000000000..3d1c1c504f
--- /dev/null
+++ b/packages/fuselage/src/components/Sidepanel/index.ts
@@ -0,0 +1,8 @@
+export * from './Sidepanel';
+export * from './SidepanelSection';
+export * from './SidepanelSectionAction';
+export * from './SidepanelHeaderTitle';
+export * from './SidepanelHeader';
+export * from './SidepanelList';
+export * from './SidepanelListItem';
+export * from './SidepanelDivider';
diff --git a/packages/fuselage/src/components/index.scss b/packages/fuselage/src/components/index.scss
index b3bd521654..1b8eee63da 100644
--- a/packages/fuselage/src/components/index.scss
+++ b/packages/fuselage/src/components/index.scss
@@ -33,6 +33,8 @@
@import './RadioButton/RadioButton.styles.scss';
@import './Select/Select.styles.scss';
@import './Sidebar/Sidebar.styles.scss';
+@import './Sidepanel/Sidepanel.styles.scss';
+@import './SidebarV2/Sidebar.styles.scss';
@import './Skeleton/Skeleton.styles.scss';
@import './States/States.styles.scss';
@import './Table/Table.styles.scss';
diff --git a/packages/fuselage/src/components/index.ts b/packages/fuselage/src/components/index.ts
index cb727b673c..b42551e738 100644
--- a/packages/fuselage/src/components/index.ts
+++ b/packages/fuselage/src/components/index.ts
@@ -62,6 +62,8 @@ export * from './Popover';
export * from './SelectInput';
export { default as Sidebar } from './Sidebar';
export * from './Sidebar';
+export * from './Sidepanel';
+export * from './SidebarV2';
export * from './Skeleton';
export * from './States';
export * from './Table';
diff --git a/packages/fuselage/src/styles/mixins/templates.scss b/packages/fuselage/src/styles/mixins/templates.scss
index c7939751eb..1b3cc66f59 100644
--- a/packages/fuselage/src/styles/mixins/templates.scss
+++ b/packages/fuselage/src/styles/mixins/templates.scss
@@ -4,7 +4,7 @@
@import './shadows.scss';
@import './states.scss';
-@mixin focus-state {
+@mixin focus-state($shadow: true) {
border-width: lengths.border-width('default');
border-style: solid;
border-color: transparent;
@@ -13,7 +13,11 @@
border-color: colors.stroke(highlight);
border-radius: lengths.border-radius(medium);
outline: 0;
- @include use-button-focus-shadow(colors.stroke(extra-light-highlight));
+
+ box-shadow: none;
+ @if $shadow {
+ @include use-focus-shadow(colors.stroke(extra-light-highlight));
+ }
}
@content;