Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: cleaning the room filter between navigation on the admin section #32152

Merged
merged 30 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
16a73ee
feat: persisting the room filter between navigation on the admin section
AllanPazRibeiro Apr 8, 2024
45cffd9
feat: changing the logic from context/provider to local storage
AllanPazRibeiro Apr 12, 2024
5e44741
fix: fixing channel name on search
AllanPazRibeiro Apr 15, 2024
fecfb19
fix: removing not necessary test
AllanPazRibeiro Apr 15, 2024
4e7731c
fix: fix changest text
AllanPazRibeiro Apr 15, 2024
c4749b7
feat: adding unit test and fixing some test locators
AllanPazRibeiro Apr 16, 2024
72f0ca5
feat: improving page selectors to the tests
AllanPazRibeiro Apr 17, 2024
e068dfc
feat: set proper roles to the select component
AllanPazRibeiro Apr 18, 2024
24862f0
chore: sanitize `MultiSelectCustom`
dougfabris Apr 18, 2024
18be844
feat: improving the test selectors
AllanPazRibeiro Apr 18, 2024
e835cf4
feat: improving the test assertions
AllanPazRibeiro Apr 19, 2024
bd2e23c
fix: setting mocking file to ts and fix unit test warnings
AllanPazRibeiro Apr 19, 2024
fefad0b
fix: adding one sigle before each to setup the the test
AllanPazRibeiro Apr 19, 2024
903b5b4
Merge branch 'develop' into fix/CORE-217/fixing-room-search-filter-pe…
AllanPazRibeiro Apr 22, 2024
2e44a61
fix: cleaning the filters after changing sections
dougfabris Apr 18, 2024
497a7ec
chore: update changeset
AllanPazRibeiro Apr 24, 2024
f69d330
fix: fix lint issues
AllanPazRibeiro Apr 24, 2024
c75ea2f
fix: fix lint issues
AllanPazRibeiro Apr 24, 2024
43a65d3
chore: changing unit test verification logic
AllanPazRibeiro Apr 26, 2024
7b76abf
feat: adding more assertions for the unit tests
AllanPazRibeiro Apr 30, 2024
2a831dc
feat: change the application to use useMemo instead useEffect to avoi…
AllanPazRibeiro May 3, 2024
db152e0
fix: lint issues
AllanPazRibeiro May 3, 2024
f9df2e3
chore: adding the mocking client wrapper into the mock package
AllanPazRibeiro May 7, 2024
0f30b24
feat: improving test
AllanPazRibeiro May 7, 2024
35aacbd
fix: lint issues
AllanPazRibeiro May 8, 2024
b216f83
Merge branch 'develop' into fix/CORE-217/fixing-room-search-filter-pe…
AllanPazRibeiro May 14, 2024
f9c3ea3
Merge branch 'develop' into fix/CORE-217/fixing-room-search-filter-pe…
AllanPazRibeiro May 29, 2024
f94fbd1
fix: some fixes in the test selectors
AllanPazRibeiro Jun 6, 2024
05765b1
chore: removing unit test
AllanPazRibeiro Jun 7, 2024
bac931e
missing review
gabriellsh Jun 7, 2024
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
5 changes: 5 additions & 0 deletions .changeset/brown-lobsters-join.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@rocket.chat/meteor': patch
---

Resolved an issue with the room type filter not being reset after navigating between admin sections.
13 changes: 10 additions & 3 deletions apps/meteor/client/views/admin/rooms/RoomsTableFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { Box, Icon, TextInput } from '@rocket.chat/fuselage';
import type { OptionProp } from '@rocket.chat/ui-client';
import { MultiSelectCustom } from '@rocket.chat/ui-client';
import { useTranslation } from '@rocket.chat/ui-contexts';
import React, { useCallback, useState } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import type { Dispatch, ReactElement, SetStateAction } from 'react';

const roomTypeFilterStructure = [
const initialRoomTypeFilterStructure = [
{
id: 'filter_by_room',
text: 'Filter_by_room',
Expand Down Expand Up @@ -49,6 +49,13 @@ const RoomsTableFilters = ({ setFilters }: { setFilters: Dispatch<SetStateAction

const [roomTypeSelectedOptions, setRoomTypeSelectedOptions] = useState<OptionProp[]>([]);

const roomTypeFilterStructure = useMemo(() => {
return initialRoomTypeFilterStructure.map((option) => ({
...option,
checked: roomTypeSelectedOptions.some((selectedOption) => selectedOption.id === option.id),
}));
}, [roomTypeSelectedOptions]);

const handleSearchTextChange = useCallback(
(event) => {
const text = event.currentTarget.value;
Expand Down Expand Up @@ -88,7 +95,7 @@ const RoomsTableFilters = ({ setFilters }: { setFilters: Dispatch<SetStateAction
</Box>
<Box minWidth='x224' m='x4'>
<MultiSelectCustom
dropdownOptions={roomTypeFilterStructure}
dropdownOptions={roomTypeFilterStructure as OptionProp[]}
defaultTitle={'All_rooms' as any}
selectedOptionsTitle='Rooms'
setSelectedOptions={handleRoomTypeChange}
Expand Down
97 changes: 97 additions & 0 deletions apps/meteor/tests/e2e/admin-room.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { faker } from '@faker-js/faker';

import { Users } from './fixtures/userStates';
import { Admin, AdminSectionsHref } from './page-objects';
import { createTargetChannel, createTargetPrivateChannel } from './utils';
import { expect, test } from './utils/test';

test.use({ storageState: Users.admin.state });

test.describe.serial('admin-rooms', () => {
let channel: string;
let privateRoom: string;
let admin: Admin;

test.beforeEach(async ({ page }) => {
admin = new Admin(page);
await page.goto('/admin/rooms');
});

test.beforeAll(async ({ browser, api }) => {
[channel, privateRoom] = await Promise.all([
createTargetChannel(api),
createTargetPrivateChannel(api),
browser.newPage({ storageState: Users.admin.state }).then(async (page) => {
await page.goto('/home');
await page.waitForSelector('[data-qa-id="home-header"]');
return page;
}),
]);
});

test('should display the Rooms Table', async ({ page }) => {
await expect(page.locator('[data-qa-type="PageHeader-title"]')).toContainText('Rooms');
});

test('should filter room by name', async ({ page }) => {
await admin.inputSearchRooms.fill(channel);

await expect(page.locator(`[qa-room-name="${channel}"]`)).toBeVisible();
});

test('should filter rooms by type', async ({ page }) => {
const dropdown = await admin.dropdownFilterRoomType();
await dropdown.click();

const privateOption = page.locator('text=Private channels');

await privateOption.waitFor();
await privateOption.click();

const selectedDropdown = await admin.dropdownFilterRoomType('Rooms (1)');
await expect(selectedDropdown).toBeVisible();

await expect(page.locator('text=Private Channel').first()).toBeVisible();
});

test('should filter rooms by type and name', async ({ page }) => {
await admin.inputSearchRooms.fill(privateRoom);

const dropdown = await admin.dropdownFilterRoomType();
await dropdown.click();

await page.locator('text=Private channels').click();

await expect(page.locator(`[qa-room-name="${privateRoom}"]`)).toBeVisible();
});

test('should be empty in case of the search does not find any room', async ({ page }) => {
const nonExistingChannel = faker.string.alpha(10);

await admin.inputSearchRooms.fill(nonExistingChannel);

const dropdown = await admin.dropdownFilterRoomType();
await dropdown.click();

await page.locator('text=Private channels').click();

await expect(page.locator('text=No results found')).toBeVisible();
});

test('should filter rooms by type and name and clean the filter after changing section', async ({ page }) => {
await admin.inputSearchRooms.fill(privateRoom);
const dropdown = await admin.dropdownFilterRoomType();
await dropdown.click();

await page.locator('text=Private channels').click();

const workspaceButton = await admin.adminSectionButton(AdminSectionsHref.Workspace);
await workspaceButton.click();

const roomsButton = await admin.adminSectionButton(AdminSectionsHref.Rooms);
await roomsButton.click();

const selectDropdown = await admin.dropdownFilterRoomType('All rooms');
await expect(selectDropdown).toBeVisible();
});
});
30 changes: 30 additions & 0 deletions apps/meteor/tests/e2e/page-objects/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,28 @@ import type { Locator, Page } from '@playwright/test';

import { AdminFlextab } from './fragments/admin-flextab';

export enum AdminSectionsHref {
Workspace = '/admin/info',
Subscription = '/admin/subscription',
Engagement = '/admin/engagement/users',
Moderation = '/admin/moderation',
Federation = '/admin/federation',
Rooms = '/admin/rooms',
Users = '/admin/users',
Invites = '/admin/invites',
User_Status = '/admin/user-status',
Permissions = '/admin/permissions',
Device_Management = '/admin/device-management',
Email_Inboxes = '/admin/email-inboxes',
Mailer = '/admin/mailer',
Third_party_login = '/admin/third-party-login',
Integrations = '/admin/integrations',
Import = '/admin/import',
Reports = '/admin/reports',
Sounds = '/admin/sounds',
Emoji = '/admin/emoji',
Settings = '/admin/settings',
}
export class Admin {
public readonly page: Page;

Expand Down Expand Up @@ -208,4 +230,12 @@ export class Admin {
getUserRowByUsername(username: string): Locator {
return this.page.locator('tr', { hasText: username })
}

async dropdownFilterRoomType(text = 'All rooms'): Promise<Locator> {
return this.page.locator(`div[role="button"]:has-text("${text}")`);
}

async adminSectionButton(href: AdminSectionsHref): Promise<Locator> {
return this.page.locator(`a[href="${href}"]`);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,9 @@ export const MultiSelectCustom = ({
<Box display='flex' flexGrow={1} position='relative'>
<MultiSelectCustomAnchor
ref={reference}
onClick={toggleCollapsed as any}
collapsed={collapsed}
onClick={() => toggleCollapsed(!collapsed)}
onKeyDown={(e) => (e.code === 'Enter' || e.code === 'Space') && toggleCollapsed(!collapsed)}
defaultTitle={defaultTitle}
selectedOptionsTitle={selectedOptionsTitle}
selectedOptionsCount={count}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,66 +1,44 @@
import { css } from '@rocket.chat/css-in-js';
import { Box, Button, Icon, Palette } from '@rocket.chat/fuselage';
import { Box, Icon } from '@rocket.chat/fuselage';
import type { TranslationKey } from '@rocket.chat/ui-contexts';
import { useTranslation } from '@rocket.chat/ui-contexts';
import type { ComponentProps } from 'react';
import { forwardRef } from 'react';

type MultiSelectCustomAnchorProps = {
onClick?: (value: boolean) => void;
collapsed: boolean;
defaultTitle: TranslationKey;
selectedOptionsTitle: TranslationKey;
selectedOptionsCount: number;
maxCount: number;
} & ComponentProps<typeof Button>;
} & ComponentProps<typeof Box>;

const MultiSelectCustomAnchor = forwardRef<HTMLElement, MultiSelectCustomAnchorProps>(function MultiSelectCustomAnchor(
{ onClick, collapsed, selectedOptionsCount, selectedOptionsTitle, defaultTitle, maxCount, ...props },
{ collapsed, selectedOptionsCount, selectedOptionsTitle, defaultTitle, maxCount, ...props },
ref,
) {
const t = useTranslation();

const inputStyle = collapsed
? css`
&,
&:hover,
&:active,
&:focus {
cursor: pointer;
border-color: ${Palette.stroke['stroke-highlight'].toString()}!important;
box-shadow: 0 0 0 2px ${Palette.shadow['shadow-highlight'].toString()};
}
`
: css`
& {
cursor: pointer;
}
`;

const customStyle = css`
&:hover {
cursor: pointer;
}
`;
const isDirty = selectedOptionsCount > 0 && selectedOptionsCount !== maxCount - 1;

return (
<Box
ref={ref}
onClick={onClick}
role='button'
tabIndex={0}
display='flex'
justifyContent='space-between'
alignItems='center'
flexDirection='row'
borderColor={Palette.stroke['stroke-light'].toString()}
borderWidth='x1'
borderRadius={4}
bg={Palette.surface['surface-light'].toString()}
h='x40'
w='full'
pb={10}
pi={16}
color={isDirty ? Palette.text['font-default'].toString() : Palette.text['font-annotation'].toString()}
className={inputStyle}
className={['rcx-input-box__wrapper', customStyle].filter(Boolean)}
{...props}
>
{isDirty ? `${t(selectedOptionsTitle)} (${selectedOptionsCount})` : t(defaultTitle)}
<Icon name='chevron-down' fontSize='x20' color='hint' />
<Icon name={collapsed ? 'chevron-up' : 'chevron-down'} fontSize='x20' color='hint' />
</Box>
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const MultiSelectCustomListWrapper = forwardRef<Element, ComponentProps<typeof B
ref,
) {
return (
<Box ref={ref} zIndex='2' w='full' position='absolute' mbs={40} pbs={4}>
<Box ref={ref} zIndex={99} w='full' position='absolute' mbs={40} pbs={4}>
{children}
</Box>
);
Expand Down
Loading