diff --git a/.changeset/fix_sidebar_badge_alignment.md b/.changeset/fix_sidebar_badge_alignment.md new file mode 100644 index 000000000..65f35747f --- /dev/null +++ b/.changeset/fix_sidebar_badge_alignment.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix sidebar notification badge positioning so unread and unverified counts align consistently. diff --git a/src/app/components/sidebar/SidebarCountBadge.test.tsx b/src/app/components/sidebar/SidebarCountBadge.test.tsx new file mode 100644 index 000000000..dbcaa5cac --- /dev/null +++ b/src/app/components/sidebar/SidebarCountBadge.test.tsx @@ -0,0 +1,36 @@ +import { render } from '@testing-library/react'; +import { describe, expect, it } from 'vitest'; +import { SidebarItem } from './SidebarItem'; +import { SidebarCountBadge } from './SidebarCountBadge'; + +describe('SidebarCountBadge', () => { + it('uses the UnverifiedTab top-left numbered badge anchor', () => { + const { container, getByText } = render(); + + expect(container.firstElementChild).toHaveStyle({ + top: '-0.375rem', + left: '-0.375rem', + right: 'auto', + }); + expect(getByText('1')).toBeInTheDocument(); + }); + + it('keeps the top-left anchor even when the parent contains a data-id button', () => { + const { container } = render( + + + + + ); + + const badge = container.querySelector('div[class*="Sidebar_SidebarItemBadge"]'); + expect(badge).not.toBeNull(); + expect(badge).toHaveStyle({ + top: '-0.375rem', + left: '-0.375rem', + right: 'auto', + }); + }); +}); diff --git a/src/app/components/sidebar/SidebarCountBadge.tsx b/src/app/components/sidebar/SidebarCountBadge.tsx new file mode 100644 index 000000000..e6ce6de29 --- /dev/null +++ b/src/app/components/sidebar/SidebarCountBadge.tsx @@ -0,0 +1,20 @@ +import { Badge, Text, toRem } from 'folds'; +import { millify } from '$plugins/millify'; +import { SidebarItemBadge } from './SidebarItem'; + +type SidebarCountBadgeProps = { + count: number; + variant: 'Warning' | 'Success' | 'Secondary'; +}; + +export function SidebarCountBadge({ count, variant }: Readonly) { + return ( + + + + {millify(count)} + + + + ); +} diff --git a/src/app/components/sidebar/index.ts b/src/app/components/sidebar/index.ts index 49e15b3e1..ba272a90f 100644 --- a/src/app/components/sidebar/index.ts +++ b/src/app/components/sidebar/index.ts @@ -1,4 +1,5 @@ export * from './Sidebar'; +export * from './SidebarCountBadge'; export * from './SidebarItem'; export * from './SidebarContent'; export * from './SidebarStack'; diff --git a/src/app/components/unread-badge/UnreadBadge.tsx b/src/app/components/unread-badge/UnreadBadge.tsx index 1ad2605ab..cb7c0b2b5 100644 --- a/src/app/components/unread-badge/UnreadBadge.tsx +++ b/src/app/components/unread-badge/UnreadBadge.tsx @@ -4,7 +4,7 @@ import { millify } from '$plugins/millify'; import { useSetting } from '$state/hooks/settings'; import { settingsAtom } from '$state/settings'; -type UnreadBadgeProps = { +export type UnreadBadgeProps = { highlight?: boolean; count: number; /** Whether this badge belongs to a DM room. Used with the badgeCountDMsOnly setting. */ diff --git a/src/app/pages/client/sidebar/AccountSwitcherTab.tsx b/src/app/pages/client/sidebar/AccountSwitcherTab.tsx index 75a7d099b..18ba4a568 100644 --- a/src/app/pages/client/sidebar/AccountSwitcherTab.tsx +++ b/src/app/pages/client/sidebar/AccountSwitcherTab.tsx @@ -27,10 +27,10 @@ import { backgroundUnreadCountsAtom, } from '$state/sessions'; import { + SidebarCountBadge, SidebarItem, SidebarItemTooltip, SidebarAvatar, - SidebarItemBadge, } from '$components/sidebar'; import { UserAvatar } from '$components/user-avatar'; import { nameInitials } from '$utils/common'; @@ -287,12 +287,10 @@ export function AccountSwitcherTab() { )} {(totalBackgroundUnread > 0 || anyBackgroundHighlight) && ( - - - + )} {unread && (unread.total > 0 || unread.highlight > 0) && ( - 0} - style={{ - left: unread.total > 0 ? toRem(-6) : toRem(-4), - right: 'auto', - }} - > - 0} - count={unread.highlight > 0 ? unread.highlight : unread.total} - dm - /> - + 0 ? 'Success' : 'Secondary'} + count={unread.highlight > 0 ? unread.highlight : unread.total} + /> )} ); diff --git a/src/app/pages/client/sidebar/DirectTab.tsx b/src/app/pages/client/sidebar/DirectTab.tsx index a1ff6ff58..362f0260f 100644 --- a/src/app/pages/client/sidebar/DirectTab.tsx +++ b/src/app/pages/client/sidebar/DirectTab.tsx @@ -12,12 +12,11 @@ import { getDirectPath, joinPathComponent } from '$pages/pathUtils'; import { useRoomsUnread } from '$state/hooks/unread'; import { SidebarAvatar, + SidebarCountBadge, SidebarItem, - SidebarItemBadge, SidebarItemTooltip, } from '$components/sidebar'; import { useDirectSelected } from '$hooks/router/useDirectSelected'; -import { UnreadBadge } from '$components/unread-badge'; import { ScreenSize, useScreenSizeContext } from '$hooks/useScreenSize'; import { useNavToActivePathAtom } from '$state/hooks/navToActivePath'; import { markAsRead } from '$utils/notifications'; @@ -115,19 +114,10 @@ export function DirectTab() { )} {directUnread && ( - 0} - style={{ - left: directUnread.total > 0 ? toRem(-6) : toRem(-4), - right: 'auto', - }} - > - 0} - count={directUnread.highlight > 0 ? directUnread.highlight : directUnread.total} - dm - /> - + 0 ? 'Success' : 'Secondary'} + count={directUnread.highlight > 0 ? directUnread.highlight : directUnread.total} + /> )} {menuAnchor && ( {homeUnread && ( - 0} - style={{ - left: homeUnread.total > 0 ? toRem(-6) : toRem(-4), - right: 'auto', - }} - > - 0} - count={homeUnread.highlight > 0 ? homeUnread.highlight : homeUnread.total} - /> - + 0 ? 'Success' : 'Secondary'} + count={homeUnread.highlight > 0 ? homeUnread.highlight : homeUnread.total} + /> )} {menuAnchor && ( )} - {inviteCount > 0 && ( - - - - )} + {inviteCount > 0 && } ); } diff --git a/src/app/pages/client/sidebar/SpaceTabs.tsx b/src/app/pages/client/sidebar/SpaceTabs.tsx index c751f00cb..5cadc0c66 100644 --- a/src/app/pages/client/sidebar/SpaceTabs.tsx +++ b/src/app/pages/client/sidebar/SpaceTabs.tsx @@ -50,8 +50,8 @@ import { allRoomsAtom } from '$state/room-list/roomList'; import { getSpaceLobbyPath, getSpacePath, joinPathComponent } from '$pages/pathUtils'; import { SidebarAvatar, + SidebarCountBadge, SidebarItem, - SidebarItemBadge, SidebarItemTooltip, SidebarStack, SidebarStackSeparator, @@ -60,7 +60,6 @@ import { } from '$components/sidebar'; import { RoomUnreadProvider, RoomsUnreadProvider } from '$components/RoomUnreadProvider'; import { useSelectedSpace } from '$hooks/router/useSelectedSpace'; -import { UnreadBadge } from '$components/unread-badge'; import { getCanonicalAliasOrRoomId, isRoomAlias } from '$utils/matrix'; import { RoomAvatar } from '$components/room-avatar'; import { nameInitials, randomStr } from '$utils/common'; @@ -465,18 +464,10 @@ function SpaceTab({ )} {unread && ( - 0} - style={{ - left: unread.total > 0 ? toRem(-6) : toRem(-4), - right: 'auto', - }} - > - 0} - count={unread.highlight > 0 ? unread.highlight : unread.total} - /> - + 0 ? 'Success' : 'Secondary'} + count={unread.highlight > 0 ? unread.highlight : unread.total} + /> )} {menuAnchor && ( {unread && ( - 0} - style={{ - left: unread.total > 0 ? toRem(-6) : toRem(-4), - right: 'auto', - }} - > - 0} - count={unread.highlight > 0 ? unread.highlight : unread.total} - /> - + 0 ? 'Success' : 'Secondary'} + count={unread.highlight > 0 ? unread.highlight : unread.total} + /> )} )} diff --git a/src/app/pages/client/sidebar/UnverifiedTab.tsx b/src/app/pages/client/sidebar/UnverifiedTab.tsx index a6c446b44..745fbdd8d 100644 --- a/src/app/pages/client/sidebar/UnverifiedTab.tsx +++ b/src/app/pages/client/sidebar/UnverifiedTab.tsx @@ -1,9 +1,9 @@ import { useState } from 'react'; -import { Badge, color, Icon, Icons, Text, toRem } from 'folds'; +import { color, Icon, Icons } from 'folds'; import { SidebarAvatar, + SidebarCountBadge, SidebarItem, - SidebarItemBadge, SidebarItemTooltip, } from '$components/sidebar'; import { useDeviceIds, useDeviceList, useSplitCurrentDevice } from '$hooks/useDeviceList'; @@ -66,13 +66,7 @@ function UnverifiedIndicator() { )} {!unverified && unverifiedDeviceCount && unverifiedDeviceCount > 0 && ( - - - - {unverifiedDeviceCount} - - - + )} )}