Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { renderHook } from '@testing-library/react';

import { useGroupingListItems } from './useGroupingListItems';

it('should render groupingList items', async () => {
const { result } = renderHook(() => useGroupingListItems());

expect(result.current[0]).toEqual(
expect.objectContaining({
id: 'unread',
}),
);

expect(result.current[1]).toEqual(
expect.objectContaining({
id: 'favorites',
}),
);

expect(result.current[2]).toEqual(
expect.objectContaining({
id: 'types',
}),
);
});
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
import { CheckBox } from '@rocket.chat/fuselage';
import type { GenericMenuItemProps } from '@rocket.chat/ui-client';
import { useFeaturePreview, type GenericMenuItemProps } from '@rocket.chat/ui-client';
import { useEndpoint, useUserPreference } from '@rocket.chat/ui-contexts';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';

export const useGroupingListItems = (): GenericMenuItemProps[] => {
const { t } = useTranslation();
const secondSidebarEnabled = useFeaturePreview('secondarySidebar');

const sidebarGroupByType = useUserPreference<boolean>('sidebarGroupByType');
const sidebarShowFavorites = useUserPreference<boolean>('sidebarShowFavorites');
const sidebarShowUnread = useUserPreference<boolean>('sidebarShowUnread');

const saveUserPreferences = useEndpoint('POST', '/v1/users.setPreferences');

const useHandleChange = (key: 'sidebarGroupByType' | 'sidebarShowUnread', value: boolean): (() => void) =>
const useHandleChange = (key: 'sidebarGroupByType' | 'sidebarShowFavorites' | 'sidebarShowUnread', value: boolean): (() => void) =>
useCallback(() => saveUserPreferences({ data: { [key]: value } }), [key, value]);

const handleChangeGroupByType = useHandleChange('sidebarGroupByType', !sidebarGroupByType);
const handleChangeShoFavorite = useHandleChange('sidebarShowFavorites', !sidebarShowFavorites);
const handleChangeShowUnread = useHandleChange('sidebarShowUnread', !sidebarShowUnread);

return [
Expand All @@ -25,11 +28,17 @@ export const useGroupingListItems = (): GenericMenuItemProps[] => {
icon: 'flag',
addon: <CheckBox onChange={handleChangeShowUnread} checked={sidebarShowUnread} />,
},
!secondSidebarEnabled && {
id: 'favorites',
content: t('Favorites'),
icon: 'star',
addon: <CheckBox onChange={handleChangeShoFavorite} checked={sidebarShowFavorites} />,
},
{
id: 'types',
content: t('Types'),
icon: 'group-by-type',
addon: <CheckBox onChange={handleChangeGroupByType} checked={sidebarGroupByType} />,
},
];
].filter(Boolean) as GenericMenuItemProps[];
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { mockAppRoot } from '@rocket.chat/mock-providers';
import { renderHook } from '@testing-library/react';

import { useGroupingListItems } from './useGroupingListItems';

it('should render groupingList items', async () => {
it('should render all groupingList items', async () => {
const { result } = renderHook(() => useGroupingListItems());

expect(result.current[0]).toEqual(
Expand All @@ -11,6 +12,33 @@ it('should render groupingList items', async () => {
}),
);

expect(result.current[1]).toEqual(
expect.objectContaining({
id: 'favorites',
}),
);

expect(result.current[2]).toEqual(
expect.objectContaining({
id: 'types',
}),
);
});

it('should render only unread and types groupingList items if secondarySidebar is enabled', async () => {
const { result } = renderHook(() => useGroupingListItems(), {
wrapper: mockAppRoot()
.withSetting('Accounts_AllowFeaturePreview', true)
.withUserPreference('featuresPreview', [{ name: 'secondarySidebar', value: true }])
.build(),
});

expect(result.current[0]).toEqual(
expect.objectContaining({
id: 'unread',
}),
);

expect(result.current[1]).toEqual(
expect.objectContaining({
id: 'types',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import type { GenericMenuItemProps } from '@rocket.chat/ui-client';
import { useFeaturePreview } from '@rocket.chat/ui-client';
import { useTranslation } from 'react-i18next';

import { useGroupingListItems } from './useGroupingListItems';
import { useSortModeItems } from './useSortModeItems';
import { useViewModeItems } from './useViewModeItems';

export const useSortMenu = () => {
const { t } = useTranslation();
const secondSidebarEnabled = useFeaturePreview('secondarySidebar');

const viewModeItems = useViewModeItems();
const sortModeItems = useSortModeItems();
const groupingListItems = useGroupingListItems();

const sections = [
return [
!secondSidebarEnabled ? { title: t('Display'), items: viewModeItems } : undefined,
{ title: t('Sort_By'), items: sortModeItems },
{ title: t('Group_by'), items: groupingListItems },
];

return sections;
].filter(Boolean) as { title: string; items: GenericMenuItemProps[] }[];
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { renderHook } from '@testing-library/react';

import { useViewModeItems } from './useViewModeItems';

it('should render viewMode items', async () => {
const { result } = renderHook(() => useViewModeItems());

expect(result.current[0]).toEqual(
expect.objectContaining({
id: 'extended',
}),
);

expect(result.current[1]).toEqual(
expect.objectContaining({
id: 'medium',
}),
);

expect(result.current[2]).toEqual(
expect.objectContaining({
id: 'condensed',
}),
);

expect(result.current[3]).toEqual(
expect.objectContaining({
id: 'avatars',
}),
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { RadioButton, ToggleSwitch } from '@rocket.chat/fuselage';
import type { GenericMenuItemProps } from '@rocket.chat/ui-client';
import { useEndpoint, useUserPreference } from '@rocket.chat/ui-contexts';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';

export const useViewModeItems = (): GenericMenuItemProps[] => {
const { t } = useTranslation();

const saveUserPreferences = useEndpoint('POST', '/v1/users.setPreferences');

const useHandleChange = (value: 'medium' | 'extended' | 'condensed'): (() => void) =>
useCallback(() => saveUserPreferences({ data: { sidebarViewMode: value } }), [value]);

const sidebarViewMode = useUserPreference<'medium' | 'extended' | 'condensed'>('sidebarViewMode', 'extended');
const sidebarDisplayAvatar = useUserPreference('sidebarDisplayAvatar', false);

const setToExtended = useHandleChange('extended');
const setToMedium = useHandleChange('medium');
const setToCondensed = useHandleChange('condensed');

const handleChangeSidebarDisplayAvatar = useCallback(
() => saveUserPreferences({ data: { sidebarDisplayAvatar: !sidebarDisplayAvatar } }),
[saveUserPreferences, sidebarDisplayAvatar],
);

return [
{
id: 'extended',
content: t('Extended'),
icon: 'extended-view',
addon: <RadioButton onChange={setToExtended} checked={sidebarViewMode === 'extended'} />,
},
{
id: 'medium',
content: t('Medium'),
icon: 'medium-view',
addon: <RadioButton onChange={setToMedium} checked={sidebarViewMode === 'medium'} />,
},
{
id: 'condensed',
content: t('Condensed'),
icon: 'condensed-view',
addon: <RadioButton onChange={setToCondensed} checked={sidebarViewMode === 'condensed'} />,
},
{
id: 'avatars',
content: t('Avatars'),
icon: 'user-rounded',
addon: <ToggleSwitch onChange={handleChangeSidebarDisplayAvatar} checked={sidebarDisplayAvatar} />,
},
];
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { usePermission, useSetting, useUserSubscription } from '@rocket.chat/ui-
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';

import { useLeaveRoomAction } from '../../hooks/menuActions/useLeaveRoom';
import { useToggleFavoriteAction } from '../../hooks/menuActions/useToggleFavoriteAction';
import { useToggleReadAction } from '../../hooks/menuActions/useToggleReadAction';
import { useHideRoomAction } from '../../hooks/useHideRoomAction';
import { useOmnichannelPrioritiesMenu } from '../../omnichannel/hooks/useOmnichannelPrioritiesMenu';
import { useLeaveRoomAction } from './menuActions/useLeaveRoom';
import { useToggleFavoriteAction } from './menuActions/useToggleFavoriteAction';
import { useToggleReadAction } from './menuActions/useToggleReadAction';
import { useHideRoomAction } from './useHideRoomAction';
import { useOmnichannelPrioritiesMenu } from '../omnichannel/hooks/useOmnichannelPrioritiesMenu';

type RoomMenuActionsProps = {
rid: string;
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/client/portals/SidebarPortal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import SidebarPortalV2 from './SidebarPortalV2';

const SidebarPortal = ({ children }: { children: ReactNode }) => {
return (
<FeaturePreview feature='newNavigation'>
<FeaturePreview feature='secondarySidebar'>
<FeaturePreviewOff>
<SidebarPortalV1 children={children} />
</FeaturePreviewOff>
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/client/sidebar/RoomMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useLayout, useTranslation } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
import { memo } from 'react';

import { useRoomMenuActions } from './hooks/useRoomMenuActions';
import { useRoomMenuActions } from '../hooks/useRoomMenuActions';

type RoomMenuProps = {
rid: string;
Expand Down
72 changes: 72 additions & 0 deletions apps/meteor/client/sidebarv2/Item/Condensed.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Box, IconButton } from '@rocket.chat/fuselage';
import { UserAvatar } from '@rocket.chat/ui-avatar';
import { action } from '@storybook/addon-actions';
import type { Meta, StoryFn } from '@storybook/react';

import Condensed from './Condensed';
import * as Status from '../../components/UserStatus';

export default {
title: 'SidebarV2/Condensed',
component: Condensed,
args: {
clickable: true,
title: 'John Doe',
},
decorators: [
(fn) => (
<Box maxWidth='x300' bg='dark' borderRadius='x4'>
{fn()}
</Box>
),
],
} satisfies Meta<typeof Condensed>;

const Template: StoryFn<typeof Condensed> = (args) => (
<Condensed
{...args}
titleIcon={
<Box mi={4}>
<Status.Online />
</Box>
}
avatar={<UserAvatar username='john.doe' size='x16' url='https://via.placeholder.com/16' />}
/>
);

export const Normal = Template.bind({});

export const Selected = Template.bind({});
Selected.args = {
selected: true,
};

export const Menu = Template.bind({});
Menu.args = {
menuOptions: {
hide: {
label: { label: 'Hide', icon: 'eye-off' },
action: action('action'),
},
read: {
label: { label: 'Mark_read', icon: 'flag' },
action: action('action'),
},
favorite: {
label: { label: 'Favorite', icon: 'star' },
action: action('action'),
},
},
};

export const Actions = Template.bind({});
Actions.args = {
actions: (
<>
<IconButton secondary success icon='phone' />
<IconButton secondary danger icon='circle-cross' />
<IconButton secondary info icon='trash' />
<IconButton secondary icon='phone' />
</>
),
};
42 changes: 42 additions & 0 deletions apps/meteor/client/sidebarv2/Item/Condensed.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { IconButton, SidebarV2Item, SidebarV2ItemAvatarWrapper, SidebarV2ItemMenu, SidebarV2ItemTitle } from '@rocket.chat/fuselage';
import type { HTMLAttributes, ReactNode } from 'react';
import { memo, useState } from 'react';

type CondensedProps = {
title: ReactNode;
titleIcon?: ReactNode;
avatar: ReactNode;
icon?: ReactNode;
actions?: ReactNode;
href?: string;
unread?: boolean;
menu?: () => ReactNode;
menuOptions?: any;
selected?: boolean;
badges?: ReactNode;
clickable?: boolean;
} & Omit<HTMLAttributes<HTMLAnchorElement>, 'is'>;

const Condensed = ({ icon, title, avatar, actions, unread, menu, badges, ...props }: CondensedProps) => {
const [menuVisibility, setMenuVisibility] = useState(!!window.DISABLE_ANIMATION);

const handleFocus = () => setMenuVisibility(true);
const handlePointerEnter = () => setMenuVisibility(true);

return (
<SidebarV2Item {...props} onFocus={handleFocus} onPointerEnter={handlePointerEnter}>
{avatar && <SidebarV2ItemAvatarWrapper>{avatar}</SidebarV2ItemAvatarWrapper>}
{icon}
<SidebarV2ItemTitle unread={unread}>{title}</SidebarV2ItemTitle>
{badges}
{actions}
{menu && (
<SidebarV2ItemMenu>
{menuVisibility ? menu() : <IconButton tabIndex={-1} aria-hidden mini rcx-sidebar-v2-item__menu icon='kebab' />}
</SidebarV2ItemMenu>
)}
</SidebarV2Item>
);
};

export default memo(Condensed);
Loading
Loading