diff --git a/e2e-tests/playwright/specs/functional/channels/search/team_selector.spec.ts b/e2e-tests/playwright/specs/functional/channels/search/team_selector.spec.ts
index 9766877957..496c2a508f 100644
--- a/e2e-tests/playwright/specs/functional/channels/search/team_selector.spec.ts
+++ b/e2e-tests/playwright/specs/functional/channels/search/team_selector.spec.ts
@@ -3,18 +3,27 @@
import {expect, test} from '@mattermost/playwright-lib';
-test('team selector must show all my teams', async ({pw}) => {
+test('team selector should not be visible if user belongs to only one team', async ({pw}) => {
+ // # Create a user with only one team
const {adminClient, user, team} = await pw.initSetup();
- // # create 2 more teams and add the user to them
- const teams = [team];
- for (let i = 0; i < 2; i++) {
- const newTeam = await adminClient.createTeam(pw.random.team('team', 'Team', 'O', true));
- await adminClient.addUsersToTeam(newTeam.id, [user.id]);
- teams.push(newTeam);
- }
+ // # Create a channel in the team
+ const channel = await adminClient.createChannel(
+ pw.random.channel({
+ teamId: team.id,
+ displayName: 'Test Channel',
+ name: 'test-channel',
+ }),
+ );
- // # Log in a user in new browser context
+ // # Post a message in the channel
+ const message = 'test message for search';
+ await adminClient.createPost({
+ channel_id: channel.id,
+ message,
+ });
+
+ // # Log in as the user
const {channelsPage} = await pw.testBrowser.login(user);
// # Visit a default channel page
@@ -24,66 +33,154 @@ test('team selector must show all my teams', async ({pw}) => {
// # Open the search UI
await channelsPage.globalHeader.openSearch();
- // * Check that the team selector is visible
+ // * Verify that the team selector is not visible in the search box
const page = channelsPage.page;
- await expect(page.getByTestId('searchTeamsSelectorMenuButton')).toBeVisible();
- // # Click on the team selector
+ // Make sure the search box is open but doesn't have a team selector
+ await expect(page.locator('#searchBox')).toBeVisible();
+ await expect(page.getByTestId('searchTeamSelector')).not.toBeVisible();
+
+ // # Now search for the message to see search results
+ await page.locator('#searchBox input').fill(message);
+ await page.keyboard.press('Enter');
+
+ // # Wait for search results to load
+ await expect(page.locator('#searchContainer')).toBeVisible();
+
+ // * Verify the team selector is not visible in search results panel
+ await expect(page.locator('.team-selector-container')).not.toBeVisible();
+});
+
+test('team selector should be visible if user belongs to multiple teams', async ({pw}) => {
+ // # Create a user and admin client
+ const {adminClient, user, team} = await pw.initSetup();
+
+ // # Create a second team and add the user to it
+ const secondTeam = await adminClient.createTeam(pw.random.team('team', 'Team', 'O', true));
+ await adminClient.addUsersToTeam(secondTeam.id, [user.id]);
+
+ // # Create a channel in the first team
+ const channel = await adminClient.createChannel(
+ pw.random.channel({
+ teamId: team.id,
+ displayName: 'Test Channel',
+ name: 'test-channel-multi',
+ }),
+ );
+
+ // # Post a message in the channel
+ const message = 'test message for multiple teams search';
+ await adminClient.createPost({
+ channel_id: channel.id,
+ message,
+ });
+
+ // # Log in as the user
+ const {channelsPage} = await pw.testBrowser.login(user);
+
+ // # Visit a default channel page
+ await channelsPage.goto(team.name);
+ await channelsPage.toBeVisible();
+
+ // # Open the search UI
+ await channelsPage.globalHeader.openSearch();
+
+ // * Verify that the team selector is visible in the search box
+ const page = channelsPage.page;
+
+ // Make sure the search box is open and has a team selector
+ await expect(page.locator('#searchBox')).toBeVisible();
+ await expect(page.getByTestId('searchTeamSelector')).toBeVisible();
+
+ // # Click on the team selector button
await page.getByTestId('searchTeamsSelectorMenuButton').click();
- // * Check that the team selector is visible
+ // * Verify that both teams are visible in the menu
const teamSelector = page.getByRole('menu', {name: 'Select team'});
await expect(teamSelector).toBeVisible();
- // * Check that the team selector has the 3 teams
- teams.forEach(async (t) => {
- await expect(teamSelector.getByText(t.display_name)).toBeVisible();
- });
- // * Check that All teams is also visible
+ await expect(teamSelector.getByText(team.display_name)).toBeVisible();
+ await expect(teamSelector.getByText(secondTeam.display_name)).toBeVisible();
await expect(teamSelector.getByText('All teams')).toBeVisible();
- // * No should be visible in the menu
- await expect(teamSelector.getByLabel('Search teams')).not.toBeVisible();
- // now create and join 3 more teams
- for (let i = 0; i < 3; i++) {
+ // # Now search for the message to see search results
+ await page.click('body', {position: {x: 0, y: 0}}); // Click away to close team selector
+ await page.locator('#searchBox input').fill(message);
+ await page.keyboard.press('Enter');
+
+ // # Wait for search results to load
+ await expect(page.locator('#searchContainer')).toBeVisible();
+
+ // * Verify the team selector is visible in search results panel
+ await expect(page.locator('.team-selector-container')).toBeVisible();
+
+ // # Click on the team selector in results panel
+ await page.locator('.team-selector-container .search-teams-selector-menu-button').click();
+
+ // * Verify that both teams are visible in the results panel team selector
+ const resultsTeamSelector = page.getByRole('menu', {name: 'Select team'});
+ await expect(resultsTeamSelector).toBeVisible();
+ await expect(resultsTeamSelector.getByText(team.display_name)).toBeVisible();
+ await expect(resultsTeamSelector.getByText(secondTeam.display_name)).toBeVisible();
+ await expect(resultsTeamSelector.getByText('All teams')).toBeVisible();
+});
+
+test('team selector should show filter input with more than 4 teams', async ({pw}) => {
+ // # Create a user and admin client
+ const {adminClient, user, team} = await pw.initSetup();
+
+ // # Create 4 more teams (for a total of 5) and add the user to them
+ const teams = [team];
+ for (let i = 0; i < 4; i++) {
const newTeam = await adminClient.createTeam(pw.random.team('team', 'Team', 'O', true));
await adminClient.addUsersToTeam(newTeam.id, [user.id]);
teams.push(newTeam);
}
- // refresh the page
+ // # Log in as the user
+ const {channelsPage} = await pw.testBrowser.login(user);
+
+ // # Visit a default channel page
await channelsPage.goto(team.name);
+ await channelsPage.toBeVisible();
// # Open the search UI
await channelsPage.globalHeader.openSearch();
- // # Click on the team selector
+ // # Verify team selector is visible and click on it
+ const page = channelsPage.page;
+ await expect(page.getByTestId('searchTeamSelector')).toBeVisible();
await page.getByTestId('searchTeamsSelectorMenuButton').click();
- // * Check that the team selector is visible
+ // # Verify the team filter input is visible with 5 teams
+ const teamSelector = page.getByRole('menu', {name: 'Select team'});
await expect(teamSelector).toBeVisible();
- // * Check that the team selector has the 6 teams
- teams.forEach(async (t) => {
- await expect(teamSelector.getByText(t.display_name)).toBeVisible();
- });
- // * Check that All teams is also visible
- await expect(teamSelector.getByText('All teams')).toBeVisible();
-
- // because there's more than 4 teams, the filter input should be visible
await expect(teamSelector.getByLabel('Search teams')).toBeVisible();
- // # Type the name of the first team
- await page.getByLabel('Search teams').fill(teams[3].display_name);
+ // # Verify all teams are visible initially
+ for (const t of teams) {
+ await expect(teamSelector.getByText(t.display_name)).toBeVisible();
+ }
- // * Check that the team selector is visible
- await expect(teamSelector).toBeVisible();
+ // # Type filter text to match only one team
+ await teamSelector.getByLabel('Search teams').fill(teams[2].display_name);
- // * Noew team [0] and [3] should be visible - 0 is visible because it was currently selected.
- await expect(teamSelector.getByText(teams[0].display_name)).toBeVisible();
- await expect(teamSelector.getByText(teams[3].display_name)).toBeVisible();
- // * Check that All teams is also visible
- await expect(teamSelector.getByText('All teams')).toBeVisible();
- // * Check that the other teams are not visible
- teams.slice(1, 3).forEach(async (t) => {
- await expect(teamSelector.getByText(t.display_name)).not.toBeVisible();
- });
+ // # Verify only the matching team is visible (and the currently selected team)
+ await expect(teamSelector.getByText(teams[0].display_name)).toBeVisible(); // Current team always visible
+ await expect(teamSelector.getByText(teams[2].display_name)).toBeVisible(); // Filtered team
+
+ // # Verify other teams are hidden
+ for (let i = 1; i < teams.length; i++) {
+ if (i !== 2) {
+ // Skip the filtered team we're expecting to see
+ await expect(teamSelector.getByText(teams[i].display_name)).not.toBeVisible();
+ }
+ }
+
+ // # Clear the filter
+ await teamSelector.getByLabel('Search teams').fill('');
+
+ // # Verify all teams are visible again
+ for (const t of teams) {
+ await expect(teamSelector.getByText(t.display_name)).toBeVisible();
+ }
});
diff --git a/webapp/channels/src/components/new_search/new_search.tsx b/webapp/channels/src/components/new_search/new_search.tsx
index caae68caad..9964aff21d 100644
--- a/webapp/channels/src/components/new_search/new_search.tsx
+++ b/webapp/channels/src/components/new_search/new_search.tsx
@@ -9,7 +9,7 @@ import styled from 'styled-components';
import {TrackCrossTeamSearchFeature, TrackCrossTeamSearchAllTeamsEvent, TrackCrossTeamSearchCurrentTeamEvent, TrackCrossTeamSearchDifferentTeamEvent} from 'mattermost-redux/constants/telemetry';
import {getCurrentChannelNameForSearchShortcut} from 'mattermost-redux/selectors/entities/channels';
import {getIsCrossTeamSearchEnabled} from 'mattermost-redux/selectors/entities/general';
-import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
+import {getCurrentTeamId, getMyTeams} from 'mattermost-redux/selectors/entities/teams';
import {trackFeatureEvent} from 'actions/telemetry_actions';
import {updateSearchTerms, showSearchResults, updateSearchType, updateSearchTeam} from 'actions/views/rhs';
@@ -114,6 +114,7 @@ const NewSearch = (): JSX.Element => {
const pluginSearch = useSelector(getSearchButtons);
const currentTeamId = useSelector(getCurrentTeamId);
const crossTeamSearchEnabled = useSelector(getIsCrossTeamSearchEnabled);
+ const myTeams = useSelector(getMyTeams);
const dispatch = useDispatch();
const [focused, setFocused] = useState(false);
@@ -318,6 +319,7 @@ const NewSearch = (): JSX.Element => {
initialSearchType={searchType}
initialSearchTeam={searchTeam}
crossTeamSearchEnabled={crossTeamSearchEnabled}
+ myTeams={myTeams}
/>
)}
diff --git a/webapp/channels/src/components/new_search/search_box.test.tsx b/webapp/channels/src/components/new_search/search_box.test.tsx
index 354db5fcac..8bfa5ee387 100644
--- a/webapp/channels/src/components/new_search/search_box.test.tsx
+++ b/webapp/channels/src/components/new_search/search_box.test.tsx
@@ -3,6 +3,8 @@
import React from 'react';
+import type {Team} from '@mattermost/types/teams';
+
import {
renderWithContext,
screen,
@@ -19,6 +21,7 @@ describe('components/new_search/SearchBox', () => {
initialSearchType: 'messages',
initialSearchTeam: 'teamId',
crossTeamSearchEnabled: true,
+ myTeams: [{id: 'team1', name: 'team1', display_name: 'Team 1', description: ''}] as Team[],
};
test('should have the focus on the input field', () => {
@@ -72,4 +75,44 @@ describe('components/new_search/SearchBox', () => {
expect(screen.getByText('Text file')).toHaveClass('selected');
expect(screen.getByText('Word Document')).not.toHaveClass('selected');
});
+
+ test('should show team selector when there is more than one team', () => {
+ const props = {
+ ...baseProps,
+ myTeams: [
+ {id: 'team1', name: 'team1', display_name: 'Team 1', description: ''} as Team,
+ {id: 'team2', name: 'team2', display_name: 'Team 2', description: ''} as Team,
+ ],
+ };
+ renderWithContext();
+
+ // The select team dropdown should be visible
+ const teamSelector = document.querySelector('div[data-testid="searchTeamSelector"]');
+ expect(teamSelector).toBeInTheDocument();
+ });
+
+ test('should not show team selector when there is only one team', () => {
+ // Base props already has one team
+ renderWithContext();
+
+ // The select team dropdown should not be visible
+ const teamSelector = document.querySelector('div[data-testid="searchTeamSelector"]');
+ expect(teamSelector).not.toBeInTheDocument();
+ });
+
+ test('should not show team selector when cross-team search is disabled', () => {
+ const props = {
+ ...baseProps,
+ crossTeamSearchEnabled: false,
+ myTeams: [
+ {id: 'team1', name: 'team1', display_name: 'Team 1', description: ''} as Team,
+ {id: 'team2', name: 'team2', display_name: 'Team 2', description: ''} as Team,
+ ],
+ };
+ renderWithContext();
+
+ // The select team dropdown should not be visible
+ const teamSelector = document.querySelector('div[data-testid="searchTeamSelector"]');
+ expect(teamSelector).not.toBeInTheDocument();
+ });
});
diff --git a/webapp/channels/src/components/new_search/search_box.tsx b/webapp/channels/src/components/new_search/search_box.tsx
index aa0764a545..1b1cec64bb 100644
--- a/webapp/channels/src/components/new_search/search_box.tsx
+++ b/webapp/channels/src/components/new_search/search_box.tsx
@@ -5,6 +5,8 @@ import React, {useState, useRef, forwardRef, useCallback, useEffect} from 'react
import {useIntl} from 'react-intl';
import styled from 'styled-components';
+import type {Team} from '@mattermost/types/teams';
+
import Constants from 'utils/constants';
import * as Keyboard from 'utils/keyboard';
import {escapeRegex} from 'utils/text_formatting';
@@ -25,6 +27,7 @@ type Props = {
initialSearchType: string;
initialSearchTeam: string;
crossTeamSearchEnabled: boolean;
+ myTeams: Team[];
};
const SearchBoxContainer = styled.div`
@@ -73,7 +76,7 @@ const SearchTeamSelector = styled.div`
const SearchBox = forwardRef(
(
- {onClose, onSearch, initialSearchTerms, initialSearchType, initialSearchTeam, crossTeamSearchEnabled}: Props,
+ {onClose, onSearch, initialSearchTerms, initialSearchType, initialSearchTeam, crossTeamSearchEnabled, myTeams}: Props,
ref: React.Ref,
): JSX.Element => {
const intl = useIntl();
@@ -83,6 +86,8 @@ const SearchBox = forwardRef(
const [searchType, setSearchType] = useState(initialSearchType || 'messages');
const [selectedOption, setSelectedOption] = useState(-1);
+ const hasMoreThanOneTeam = myTeams.length > 1;
+
const inputRef = useRef(null);
const [showFilterHaveBeenReset, setShowFilterHaveBeenReset] = useState(false);
@@ -282,8 +287,8 @@ const SearchBox = forwardRef(
searchType={searchType}
setSearchType={setSearchType}
/>
- {crossTeamSearchEnabled && (
-
+ {crossTeamSearchEnabled && hasMoreThanOneTeam && (
+ getSearchTeam(state));
+ const myTeams = useSelector(getMyTeams);
+ const hasMoreThanOneTeam = myTeams.length > 1;
// REFS to the tabs so there is ability to pass the custom A11y focus event
const messagesTabRef = useRef(null);
@@ -130,7 +134,7 @@ export default function MessagesOrFilesSelector(props: Props): JSX.Element {
)}
- {props.crossTeamSearchEnabled && (
+ {props.crossTeamSearchEnabled && hasMoreThanOneTeam && (