Initial version of the new search interface (#27301)

* Initial version of the new search interface

* Fixing CI

* style tweaks

* tweaks to date picker in search

* change search input to button

* Addressing a lot of small problems

* Addressing current search visualization

* Fixing problem on filter autocompletion

* Adding some more fixes

* Tiny style ajustment

* Removing unnecessary slice call

* Adding search extensibility

* Improving the extensibility

* Fixing and refactoring a bit

* On clear click, clear the current search

* splitting components for simplicity

* tweaks to file ext search

* Addressing PR review comments and adding color to the extensions search icons

* Improving a bit the extension search

* Fixing CI

* Fixing some bugs

* Keep the focus on search input whenever we change the search type

* Adding SearchHint tests

* Adding search_box_type_selector tests

* Adding tests for the SearchBox component

* Adding tests for search box suggestions

* Adding SearchBoxInput tests

* Splitting extension suggestion component and provider

* ExtensionSuggestion tests

* Removing unneeded property

* Adding SearchBoxhints tests

* Adding tests for NewSearch component

* Adding tests for the extension suggestion provider

* Fixing linter errors

* Fixing tests

* Fixing linter errors

* Fixing linter errors

* Adding license check for plugins

* Addressing PR review comments

* Addressing PR review comments

* Addressing PR review comments

* Fixing tests

* Fixing some cypress tests

* More work around cypress tests for search

* Some accessibility changes

* more work on cypress tests

* fixing more cypress tests

* Fixing linter errors

* Fixing tests

* Fixing ctrl+f6 switching

* Fixing scrolling on small windows wheneve you are searching

* Improve editing in the middle of the search text

* Addressing some PR review comments

* Addressing some PR review comments

* Addressing some PR review comments

* Addressing some PR review comments

* Fixing tests and linter errors

* Fixingls tests and linter errors

* Fix type checks

* Clearer logic to render plugin or builtin suggestion

* Addressing PR review changes

* Addressing PR review changes

* Using selectors for plugin components

* Changing uppercase to lowercase on lists of components

* Updating tests

* Updating snapshots

* minor css tweaks

* minor css tweak

* Adding the search type and the clear button

* Fixing problem with the filetype selector

* fixing linter errors

* Some fixes

* More linter fixes

* Reverting prettier auto-formating

* Fixing CI

* Fixing CI

* Fixing chrome problem

* Fixing CI

* Addressing PR review comments

---------

Co-authored-by: Matthew Birtch <mattbirtch@gmail.com>
Co-authored-by: Christopher Speller <crspeller@gmail.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
Jesús Espino
2024-10-14 12:56:35 +02:00
коммит произвёл GitHub
родитель b7ccd745c8
Коммит 5f8bdba459
71 изменённых файлов: 2503 добавлений и 291 удалений

Просмотреть файл

@@ -25,11 +25,13 @@ describe('Search', () => {
// * X should not be visible on empty input
cy.uiGetSearchContainer().find('.input-clear-x').should('not.exist');
cy.uiGetSearchContainer().click();
// # Write something on the input
cy.uiGetSearchBox().clear().type('abc');
cy.uiGetSearchBox().find('input').clear().type('abc');
// * The input should contain what we wrote
cy.uiGetSearchBox().should('have.value', 'abc');
cy.uiGetSearchBox().find('input').should('have.value', 'abc');
// * The X should be visible
// # Then click X to clear the input field
@@ -50,7 +52,7 @@ describe('Search', () => {
// * Verify search input field exists and not search button, as inputs contains placeholder not buttons/icons
// and then type in a search text
cy.uiGetSearchBox().click().wait(TIMEOUTS.HALF_SEC).type(searchText);
cy.uiGetSearchBox().click().wait(TIMEOUTS.HALF_SEC).type(searchText + '{enter}');
// # Click on the pinned post button from the header
cy.uiGetChannelPinButton().click();

Просмотреть файл

@@ -29,9 +29,11 @@ describe('Search', () => {
cy.uiGetSearchContainer().should('be.visible').click();
cy.uiGetSearchBox().
type(`${term}{enter}`).
wait(TIMEOUTS.ONE_SEC).
clear();
cy.get('#searchbar-help-popup').should('be.visible');
wait(TIMEOUTS.ONE_SEC);
cy.uiGetSearchContainer().should('be.visible').click();
cy.uiGetSearchBox().clear();
cy.get('#searchHints').should('be.visible');
cy.uiGetSearchContainer().type('{esc}');
// # Verify the Search side bar opens up
@@ -43,13 +45,17 @@ describe('Search', () => {
cy.uiGetRHS({visible: false});
// # Verify that the cleared search text does not appear on the search box
cy.uiGetSearchBox().should('be.empty');
cy.uiGetSearchContainer().should('be.visible').click();
cy.uiGetSearchBox().find('input').should('be.empty');
cy.uiGetSearchContainer().type('{esc}');
// # Click the pin icon to open the pinned messages RHS
cy.uiGetChannelPinButton().click();
cy.uiGetRHS().should('contain', 'Pinned messages');
// # Verify that the Search term input box is still cleared and search term does not reappear when RHS opens
cy.uiGetSearchBox().and('be.empty');
cy.uiGetSearchContainer().should('be.visible').click();
cy.uiGetSearchBox().find('input').and('be.empty');
cy.uiGetSearchContainer().type('{esc}');
});
});

Просмотреть файл

@@ -36,8 +36,11 @@ describe('Search', () => {
// # Post a message
cy.postMessage(testSearch);
cy.uiGetSearchContainer().click();
// # Search the world 'hello' that is generated by /test command
cy.get('#searchBox').click().type('hello{enter}').wait(TIMEOUTS.HALF_SEC).should('have.value', 'hello');
cy.uiGetSearchBox().find('input').type('hello{enter}').wait(TIMEOUTS.HALF_SEC);
cy.get('#search-items-container span').should('contain.text', 'hello');
// # RHS should be visible with search results
cy.get('#search-items-container').should('be.visible');
@@ -52,14 +55,10 @@ describe('Search', () => {
// # Jump to conversation
cy.wrap($result).get('a.search-item__jump').first().click();
// * Search query clear icon is still present
cy.get('.input-clear.visible').should('be.visible');
cy.uiGetSearchContainer().click();
// # Hover search query clear icon
cy.get('.input-clear-x').first().trigger('mouseover', {force: true}).then(($span) => {
// * Assert that tooltip has shown
cy.wrap($span).should('have.attr', 'aria-describedby', 'InputClearTooltip');
// # Click the clear query icon
cy.wrap($span).click({force: true});

Просмотреть файл

@@ -37,23 +37,23 @@ describe('Search', () => {
// # Post a message
cy.postMessage(searchWord);
cy.uiGetSearchContainer().click();
// * Search word in searchBox and validate searchWord
cy.get('#searchBox').click().type(searchWord + '{enter}').should('have.value', searchWord);
cy.uiGetSearchBox().find('input').type(searchWord + '{enter}');
cy.uiGetSearchContainer().click();
cy.uiGetSearchBox().find('input').should('have.value', searchWord);
// # Click on "x" displayed on searchbox
cy.get('#searchbarContainer').should('be.visible').within(() => {
cy.get('#searchFormContainer').find('.input-clear-x').wait(TIMEOUTS.ONE_SEC).click({force: true});
cy.get('#searchbar-help-popup').should('be.visible');
cy.get('#searchBox').type('{esc}');
});
cy.uiGetSearchBox().find('.input-clear-x').wait(TIMEOUTS.ONE_SEC).click({force: true});
cy.uiGetSearchBox().find('#searchHints').should('be.visible');
cy.uiGetSearchBox().type('{esc}');
// # RHS should be visible with search results
cy.get('#search-items-container').should('be.visible');
// # Click on searchbox
cy.get('#searchbarContainer').should('be.visible').within(() => {
cy.get('#searchBox').click();
});
cy.uiGetSearchContainer().click();
assertSearchHintFilesOrMessages();
});
@@ -74,29 +74,28 @@ describe('Search', () => {
// # Visit town-square.
cy.visit(`/${testTeam.name}/channels/town-square`);
cy.uiGetSearchContainer().click();
// # Search for posts from that user
cy.get('#searchBox').click().type(testSearch, {force: true});
cy.uiGetSearchBox().find('input').type(testSearch, {force: true}).wait(TIMEOUTS.HALF_SEC);
// # Select user from suggestion list
cy.contains('.suggestion-list__item', `@${testUser.username}`).scrollIntoView().click({force: true});
// # Verify that search box has the updated query
cy.get('#searchBox').should('have.value', `FROM:${testUser.username} `);
cy.uiGetSearchBox().find('input').should('have.value', `FROM:${testUser.username} `);
// # Perform search
cy.get('#searchBox').click().type('{enter}').wait(TIMEOUTS.HALF_SEC);
cy.uiGetSearchBox().find('input').type('{enter}').wait(TIMEOUTS.HALF_SEC);
// * Assert that RHS should be visible with search results
cy.get('#search-items-container').should('be.visible');
// * Search query clear icon is still present
cy.get('.input-clear.visible').should('be.visible');
cy.uiGetSearchContainer().click();
// # Hover search query clear icon
cy.get('.input-clear-x').first().trigger('mouseover', {force: true}).then(($span) => {
// * Assert that tooltip has shown
cy.wrap($span).should('have.attr', 'aria-describedby', 'InputClearTooltip');
// # Click the clear query icon
cy.wrap($span).click({force: true});
@@ -111,15 +110,13 @@ describe('Search', () => {
cy.postMessage('hello');
// # Click on searchbox
cy.get('#searchbarContainer').should('be.visible').within(() => {
cy.get('input#searchBox').should('be.visible').click();
});
cy.uiGetSearchContainer().should('be.visible').click();
// * Check the contents in search options
assertSearchHintFilesOrMessages();
// # Search for search term in:
cy.get('#searchBox').click().type('in:');
cy.uiGetSearchBox().find('input').type('in:');
// # Select option from suggestion list
cy.get('.suggestion-list__item').first().click({force: true});
@@ -131,31 +128,31 @@ describe('Search', () => {
cy.get('.input-clear-x').first().click({force: true}).wait(TIMEOUTS.HALF_SEC);
// # Search for search term in:town-square{space}
cy.get('#searchBox').click().type('in:town-square ').wait(TIMEOUTS.HALF_SEC);
cy.uiGetSearchBox().find('input').type('in:town-square ').wait(TIMEOUTS.HALF_SEC);
// * Check the hint contents are now visible
assertSearchHint();
// # Clear search box
cy.get('.input-clear-x').first().click({force: true}).wait(TIMEOUTS.HALF_SEC);
cy.uiGetSearchBox().get('.input-clear-x').first().click({force: true}).wait(TIMEOUTS.HALF_SEC);
// # Search for search term in:town-square{enter}
cy.get('#searchBox').click().type('in:town-square').wait(TIMEOUTS.HALF_SEC);
cy.uiGetSearchBox().find('input').type('in:town-square').wait(TIMEOUTS.HALF_SEC);
// * Assert that channel name displays appropriately
cy.get('.suggestion-list__item').first().should('contain.text', 'Town Square~town-square');
// # Press enter to register search term
cy.get('#searchBox').click().type('{enter}');
cy.uiGetSearchBox().find('input').type('{enter}');
// * Check the hint contents are now visible
assertSearchHint();
// * Assert that searchBox now includes a trailing space
cy.get('#searchBox').should('have.value', 'in:town-square ');
cy.uiGetSearchBox().find('input').should('have.value', 'in:town-square ');
// # Perform the search
cy.get('#searchBox').click().type('{enter}').wait(TIMEOUTS.HALF_SEC);
cy.uiGetSearchBox().find('input').type('{enter}').wait(TIMEOUTS.HALF_SEC);
// * Assert autocomplete list is gone
cy.get('.suggestion-list__item').should('not.exist');
@@ -167,8 +164,10 @@ describe('Search', () => {
// # Post message
cy.postMessage(testMessage);
cy.uiGetSearchContainer().click();
// # Search for `Hell*`
cy.get('input#searchBox').click().type('Hell*{enter}').wait(TIMEOUTS.HALF_SEC);
cy.uiGetSearchBox().find('input').type('Hell*{enter}').wait(TIMEOUTS.HALF_SEC);
// # RHS should be visible with search results
cy.get('#search-items-container').should('be.visible');
@@ -180,22 +179,9 @@ describe('Search', () => {
});
const assertSearchHintFilesOrMessages = () => {
cy.get('#searchbar-help-popup').should('be.visible').within(() => {
cy.get('div span').first().should('have.text', 'What are you searching for?');
cy.get('div button:first-child span').first().should('have.text', 'Messages');
cy.get('div button:last-child span').first().should('have.text', 'Files');
});
cy.get('#searchHints').should('be.visible');
};
const assertSearchHint = () => {
cy.get('#searchbar-help-popup').should('be.visible').within(() => {
cy.get('div span').first().should('have.text', 'Search options');
cy.get('div ul li').first().should('have.text', 'From:Messages from a user');
cy.get('div ul li').eq(1).should('have.text', 'In:Messages in a channel');
cy.get('div ul li').eq(2).should('have.text', 'On:Messages on a date');
cy.get('div ul li').eq(3).should('have.text', 'Before:Messages before a date');
cy.get('div ul li').eq(4).should('have.text', 'After:Messages after a date');
cy.get('div ul li').eq(5).should('have.text', '—Exclude search terms');
cy.get('div ul li').last().should('have.text', '""Messages with phrases');
});
cy.get('#searchHints').should('be.visible');
};

Просмотреть файл

@@ -36,7 +36,8 @@ describe('Search', () => {
cy.postMessage(message);
// # Search for "apple"
cy.get('#searchBox').should('be.visible').type(apple).type('{enter}');
cy.uiGetSearchContainer().click();
cy.uiGetSearchBox().find('input').should('be.visible').type(apple).type('{enter}');
// # Get last postId
cy.getLastPostId().as('lastPostId');
@@ -46,8 +47,10 @@ describe('Search', () => {
verifySearchResult(1, postId, message, apple);
});
cy.uiGetSearchContainer().click();
// * Type banana on search box but don't hit search
cy.get('#searchBox').clear({force: true}).type(banana, {force: true});
cy.uiGetSearchBox().find('input').clear({force: true}).type(banana, {force: true});
// * Search result should not change and remain as one result with highlight still on apple
cy.get('@lastPostId').then((postId) => {

Просмотреть файл

@@ -1,70 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @search
describe('Search', () => {
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
});
});
it('MM-T4945_1 - Search bar popup should be visible when focus changes to one of its buttons', () => {
//# clicks on the search box
cy.uiGetSearchBox().click();
//* search bar popup should be visibl
cy.get('#searchbar-help-popup').should('be.visible');
//# moves the focus to the next button (in this case Messages)
cy.focused().tab();
//* search bar popup should be visible
cy.get('#searchbar-help-popup').should('be.visible');
});
it('MM-T4945_2 - Search bar popup should be hidden when focus is out of search bar items', () => {
//# clicks on the search box
cy.uiGetSearchBox().click();
//* search bar popup should be visibl
cy.get('#searchbar-help-popup').should('be.visible');
//# press tab three times to move focus out of the search bar componment
cy.focused().tab();
cy.focused().tab();
cy.focused().tab();
//* now the popup should be closed
cy.get('#searchbar-help-popup').should('not.be.visible');
});
it('MM-T4945_3 - Search bar popup should be open when focus back to search box', () => {
//# clicks on the search box
cy.uiGetSearchBox().click();
//* search bar popup should be visibl
cy.get('#searchbar-help-popup').should('be.visible');
//# moves the focus to the next button (in this case Messages)
cy.focused().tab();
//* search bar popup should be visible
cy.get('#searchbar-help-popup').should('be.visible');
//# moves the focus back to the search box
cy.focused().tab({shift: true});
//* search bar popup should still be visible
cy.get('#searchbar-help-popup').should('be.visible');
});
});

Просмотреть файл

@@ -51,25 +51,24 @@ describe('Search', () => {
// # Post a message
cy.postMessage(message);
cy.uiGetSearchContainer().should('be.visible').click();
//# Type "in:" text in search input
cy.get('#searchBox').type('in:');
cy.uiGetSearchBox().find('input').type('in:');
const sortedUsernames = groupMembers.
map((member) => member.username).
sort((a, b) => a.localeCompare(b, 'en', {numeric: true}));
//# Search group members in the menu
cy.get('#search-autocomplete__popover').should('be.visible').within(() => {
cy.findAllByTestId('listItem').contains(sortedUsernames.join(',')).click();
});
cy.uiGetSearchBox().find('.suggestion-list__main').contains(sortedUsernames.join(',')).click();
//# Press enter to select
cy.get('#searchBox').type('{enter}');
cy.uiGetSearchBox().find('input').type('{enter}');
//# Search for the message
cy.get('#searchbarContainer').should('be.visible').within(() => {
cy.get('#searchBox').clear().type(`${message}{enter}`);
});
cy.uiGetSearchContainer().should('be.visible').click();
cy.uiGetSearchBox().find('input').clear().type(`${message}{enter}`);
// * Should return exactly one result from the group channel and matches the message
cy.findAllByTestId('search-item-container').should('be.visible').and('have.length', 1).within(() => {
@@ -89,25 +88,20 @@ describe('Search', () => {
cy.get('.post-image__thumbnail').should('be.visible');
cy.uiGetPostTextBox().clear().type('{enter}');
cy.uiGetSearchContainer().should('be.visible').click();
//# Type "in:" text in search input
cy.get('#searchBox').type('in:');
cy.uiGetSearchBox().find('input').type('in:');
const sortedUsernames = groupMembers.
map((member) => member.username).
sort((a, b) => a.localeCompare(b, 'en', {numeric: true}));
//# Search group members in the menu
cy.get('#search-autocomplete__popover').should('be.visible').within(() => {
cy.findAllByTestId('listItem').contains(sortedUsernames.join(',')).click();
});
//# Press enter to select
cy.get('#searchBox').type('{enter}');
cy.uiGetSearchBox().find('.suggestion-list__main').contains(sortedUsernames.join(',')).click();
//# Search for the message
cy.get('#searchbarContainer').should('be.visible').within(() => {
cy.get('#searchBox').type(' word-file{enter}');
});
cy.uiGetSearchBox().find('input').type('word-file{enter}');
// # Click the files tab
cy.get('.files-tab').should('be.visible').click();

Просмотреть файл

@@ -9,6 +9,8 @@
// Group: @channels @search @smoke @filesearch
import * as TIMEOUTS from '../../../fixtures/timeouts';
/**
* create new DM channel
* @param {String} text - DM channel name
@@ -19,7 +21,7 @@ function createNewDMChannel(channelname) {
cy.get('#selectItems input').typeWithForce(channelname);
cy.contains('.more-modal__description', channelname).click({force: true});
cy.get('#saveItems').click();
cy.get('#saveItems').click().wait(TIMEOUTS.ONE_SEC);
}
describe('Search in DMs', () => {
@@ -57,17 +59,19 @@ describe('Search in DMs', () => {
cy.get('.post-image__thumbnail').should('be.visible');
cy.uiGetPostTextBox().clear().type('{enter}');
cy.uiGetSearchContainer().should('be.visible').click();
// # Type `in:` in searchbox
cy.get('#searchBox').type('in:');
cy.uiGetSearchBox().find('input').type('in:');
// # Select user from suggestion list
cy.contains('.suggestion-list__item', `@${otherUser.username}`).scrollIntoView().click();
// # Validate searchbox contains the username
cy.get('#searchBox').should('have.value', 'in:@' + otherUser.username + ' ');
cy.uiGetSearchBox().find('input').should('have.value', 'in:@' + otherUser.username + ' ');
// # Press Enter in searchbox
cy.get('#searchBox').type('word-file.doc').type('{enter}');
cy.uiGetSearchBox().find('input').type('word-file').type('{enter}');
// # Click the files tab
cy.get('.files-tab').should('be.visible').click();

Просмотреть файл

@@ -59,16 +59,17 @@ describe('Search in DMs', () => {
cy.postMessage(message);
// # Type `in:` in searchbox
cy.get('#searchBox').type('in:');
cy.uiGetSearchContainer().should('be.visible').click();
cy.uiGetSearchBox().find('input').type('in:');
// # Select user from suggestion list
cy.contains('.suggestion-list__item', `@${otherUser.username}`).scrollIntoView().click();
// # Validate searchbox contains the username
cy.get('#searchBox').should('have.value', 'in:@' + otherUser.username + ' ');
cy.uiGetSearchBox().find('input').should('have.value', 'in:@' + otherUser.username + ' ');
// # Press Enter in searchbox
cy.get('#searchBox').type(message).type('{enter}');
cy.uiGetSearchBox().find('input').type(message).type('{enter}');
// # Search message in each filtered result
cy.get('#search-items-container').find('.search-highlight').each(($el) => {