[MM-63007][MM-63004][MM-63020][MM-63009][MM-63008] More accessibility fixes around Search (#31409)
* [MM-63008] Make collapse button on search bar an actual button * [MM-63004][MM-63020] Convert search box to floating-ui, fix some of the roles and labels that were incorrect * [MM-63009] Add radiogroup and radio roles to the search box types * [MM-63007] Ensure search box reads out number of results with suggestion items * Fix playwright tests * PR feedback * Remove floating ui overlay * Remove unnecessary .first() by being more specific about the search box --------- Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
e367872c0b
Коммит
df1b278f62
@@ -126,7 +126,7 @@ describe('Verify Quick Navigation support across different regions in the app',
|
|||||||
it('MM-T1460_8 Verify Navigation Support in Search Results', () => {
|
it('MM-T1460_8 Verify Navigation Support in Search Results', () => {
|
||||||
// # Search for some text
|
// # Search for some text
|
||||||
cy.uiGetSearchContainer().click();
|
cy.uiGetSearchContainer().click();
|
||||||
cy.uiGetSearchBox().should('be.visible').first().type('hello {enter}');
|
cy.uiGetSearchBox().should('be.visible').type('hello {enter}');
|
||||||
|
|
||||||
// # Change the focus to search results
|
// # Change the focus to search results
|
||||||
cy.get('#searchContainer').within(() => {
|
cy.get('#searchContainer').within(() => {
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ describe('Archived channels', () => {
|
|||||||
// # Search for "Test archive reaction"
|
// # Search for "Test archive reaction"
|
||||||
cy.get('body').type('{esc}');
|
cy.get('body').type('{esc}');
|
||||||
cy.uiGetSearchContainer().click();
|
cy.uiGetSearchContainer().click();
|
||||||
cy.uiGetSearchBox().should('be.visible').first().type(messageText).type('{enter}');
|
cy.uiGetSearchBox().should('be.visible').type(messageText).type('{enter}');
|
||||||
|
|
||||||
// # Click on post dot menu so we can check for reaction icon
|
// # Click on post dot menu so we can check for reaction icon
|
||||||
cy.clickPostDotMenu(postId, 'SEARCH');
|
cy.clickPostDotMenu(postId, 'SEARCH');
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ describe('archive tests while preventing viewing archived channels', () => {
|
|||||||
|
|
||||||
// # Search for the post from step 1')
|
// # Search for the post from step 1')
|
||||||
cy.uiGetSearchContainer().click();
|
cy.uiGetSearchContainer().click();
|
||||||
cy.uiGetSearchBox().first().clear().type(`${messageText}{enter}`);
|
cy.uiGetSearchBox().clear().type(`${messageText}{enter}`);
|
||||||
|
|
||||||
// * Post is returned by search, since it's not archived anymore
|
// * Post is returned by search, since it's not archived anymore
|
||||||
cy.get('#searchContainer').should('be.visible');
|
cy.get('#searchContainer').should('be.visible');
|
||||||
@@ -96,7 +96,7 @@ describe('archive tests while preventing viewing archived channels', () => {
|
|||||||
|
|
||||||
// # Archive dialogue message reads "This will archive the channel from the team and make its contents inaccessible for all users" (Mobile dialogue makes no mention of the data will be accessible)
|
// # Archive dialogue message reads "This will archive the channel from the team and make its contents inaccessible for all users" (Mobile dialogue makes no mention of the data will be accessible)
|
||||||
cy.uiGetSearchContainer().click();
|
cy.uiGetSearchContainer().click();
|
||||||
cy.uiGetSearchBox().first().clear().type(`${testArchivedMessage}{enter}`);
|
cy.uiGetSearchBox().clear().type(`${testArchivedMessage}{enter}`);
|
||||||
|
|
||||||
// * Post is not returned by search
|
// * Post is not returned by search
|
||||||
cy.get('#searchContainer').should('be.visible');
|
cy.get('#searchContainer').should('be.visible');
|
||||||
@@ -112,7 +112,7 @@ describe('archive tests while preventing viewing archived channels', () => {
|
|||||||
|
|
||||||
// # Search for the string of text from step 1
|
// # Search for the string of text from step 1
|
||||||
cy.uiGetSearchContainer().click();
|
cy.uiGetSearchContainer().click();
|
||||||
cy.uiGetSearchBox().first().clear().type(`${messageText}{enter}`);
|
cy.uiGetSearchBox().clear().type(`${messageText}{enter}`);
|
||||||
|
|
||||||
// * Post is returned by search, since it's not archived anymore
|
// * Post is returned by search, since it's not archived anymore
|
||||||
cy.get('#searchContainer').should('be.visible');
|
cy.get('#searchContainer').should('be.visible');
|
||||||
@@ -131,7 +131,7 @@ describe('archive tests while preventing viewing archived channels', () => {
|
|||||||
// # Archive a channel and make a mental note of the channel name
|
// # Archive a channel and make a mental note of the channel name
|
||||||
// # Type "in:" and note the list of channels that appear
|
// # Type "in:" and note the list of channels that appear
|
||||||
cy.uiGetSearchContainer().click();
|
cy.uiGetSearchContainer().click();
|
||||||
cy.uiGetSearchBox().first().clear().type(`in:${testChannel.name}`);
|
cy.uiGetSearchBox().clear().type(`in:${testChannel.name}`);
|
||||||
cy.findByTestId(testChannel.name).should('not.exist');
|
cy.findByTestId(testChannel.name).should('not.exist');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ describe('Leave an archived channel', () => {
|
|||||||
|
|
||||||
// # Search for a post in an archived channel
|
// # Search for a post in an archived channel
|
||||||
cy.uiGetSearchContainer().click();
|
cy.uiGetSearchContainer().click();
|
||||||
cy.uiGetSearchBox().first().clear().type(`${testArchivedMessage}{enter}`);
|
cy.uiGetSearchBox().clear().type(`${testArchivedMessage}{enter}`);
|
||||||
|
|
||||||
// # Open the archived channel by selecting Jump from search results and then selecting the link to move to the most recent posts in the channel
|
// # Open the archived channel by selecting Jump from search results and then selecting the link to move to the most recent posts in the channel
|
||||||
cy.uiGetSearchContainer().should('be.visible');
|
cy.uiGetSearchContainer().should('be.visible');
|
||||||
@@ -113,7 +113,7 @@ describe('Leave an archived channel', () => {
|
|||||||
createArchivedChannel({prefix: 'archived-search-for'}, messageList).then(({name}) => {
|
createArchivedChannel({prefix: 'archived-search-for'}, messageList).then(({name}) => {
|
||||||
// # Locate the post in a search
|
// # Locate the post in a search
|
||||||
cy.uiGetSearchContainer().click();
|
cy.uiGetSearchContainer().click();
|
||||||
cy.uiGetSearchBox().first().clear().type(`${messageList[1]}{enter}`);
|
cy.uiGetSearchBox().clear().type(`${messageList[1]}{enter}`);
|
||||||
|
|
||||||
// # Click jump to open an archive post in permalink view
|
// # Click jump to open an archive post in permalink view
|
||||||
cy.uiGetSearchContainer().should('be.visible');
|
cy.uiGetSearchContainer().should('be.visible');
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ describe('Leave an archived channel', () => {
|
|||||||
|
|
||||||
// # Search for content from an archived channel
|
// # Search for content from an archived channel
|
||||||
cy.uiGetSearchContainer().click();
|
cy.uiGetSearchContainer().click();
|
||||||
cy.uiGetSearchBox().first().clear().type(`${messageD}{enter}`);
|
cy.uiGetSearchBox().clear().type(`${messageD}{enter}`);
|
||||||
|
|
||||||
// # Open the channel from search results
|
// # Open the channel from search results
|
||||||
cy.get('#searchContainer').should('be.visible');
|
cy.get('#searchContainer').should('be.visible');
|
||||||
@@ -109,7 +109,7 @@ describe('Leave an archived channel', () => {
|
|||||||
|
|
||||||
// # Search for content from a different archived channel
|
// # Search for content from a different archived channel
|
||||||
cy.uiGetSearchContainer().click();
|
cy.uiGetSearchContainer().click();
|
||||||
cy.uiGetSearchBox().first().clear().type(`${messageC}{enter}`);
|
cy.uiGetSearchBox().clear().type(`${messageC}{enter}`);
|
||||||
|
|
||||||
// # Open the channel from search result by clicking Jump
|
// # Open the channel from search result by clicking Jump
|
||||||
cy.get('#searchContainer').should('be.visible').findByText('Jump').click().wait(TIMEOUTS.ONE_SEC);
|
cy.get('#searchContainer').should('be.visible').findByText('Jump').click().wait(TIMEOUTS.ONE_SEC);
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ describe('Channel RHS', () => {
|
|||||||
|
|
||||||
// # Enter the search terms and hit enter to start the search
|
// # Enter the search terms and hit enter to start the search
|
||||||
cy.uiGetSearchContainer().click();
|
cy.uiGetSearchContainer().click();
|
||||||
cy.uiGetSearchBox().first().clear().type(MESSAGES.TINY).type('{enter}');
|
cy.uiGetSearchBox().clear().type(MESSAGES.TINY).type('{enter}');
|
||||||
|
|
||||||
// * Verify that the search results is opened in RHS
|
// * Verify that the search results is opened in RHS
|
||||||
verifyRHSisOpenAndHasTitle('Search Results');
|
verifyRHSisOpenAndHasTitle('Search Results');
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ describe('Verify Accessibility Support in different input fields', () => {
|
|||||||
cy.uiGetSearchBox().type('from:').wait(TIMEOUTS.ONE_SEC);
|
cy.uiGetSearchBox().type('from:').wait(TIMEOUTS.ONE_SEC);
|
||||||
|
|
||||||
// # Trigger the user autocomplete again
|
// # Trigger the user autocomplete again
|
||||||
cy.uiGetSearchBox().first().clear().type('from:').wait(TIMEOUTS.ONE_SEC).type('{downarrow}{downarrow}');
|
cy.uiGetSearchBox().clear().type('from:').wait(TIMEOUTS.ONE_SEC).type('{downarrow}{downarrow}');
|
||||||
|
|
||||||
// * Verify Accessibility Support in search autocomplete
|
// * Verify Accessibility Support in search autocomplete
|
||||||
verifySearchAutocomplete(2);
|
verifySearchAutocomplete(2);
|
||||||
@@ -103,10 +103,10 @@ describe('Verify Accessibility Support in different input fields', () => {
|
|||||||
verifySearchAutocomplete(3);
|
verifySearchAutocomplete(3);
|
||||||
|
|
||||||
// # Type the in: filter and ensure channel list is cached once
|
// # Type the in: filter and ensure channel list is cached once
|
||||||
cy.uiGetSearchBox().first().clear().type('in:').wait(TIMEOUTS.ONE_SEC);
|
cy.uiGetSearchBox().clear().type('in:').wait(TIMEOUTS.ONE_SEC);
|
||||||
|
|
||||||
// # Trigger the channel autocomplete again
|
// # Trigger the channel autocomplete again
|
||||||
cy.uiGetSearchBox().first().clear().type('in:').wait(TIMEOUTS.ONE_SEC).type('{downarrow}{downarrow}');
|
cy.uiGetSearchBox().clear().type('in:').wait(TIMEOUTS.ONE_SEC).type('{downarrow}{downarrow}');
|
||||||
|
|
||||||
// * Verify Accessibility Support in search autocomplete
|
// * Verify Accessibility Support in search autocomplete
|
||||||
verifySearchAutocomplete(2, 'channel');
|
verifySearchAutocomplete(2, 'channel');
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ describe('Upload Files - Settings', () => {
|
|||||||
cy.get('#searchHints').find('.search-hint__search-type-selector button > .icon-file-text-outline').should('not.exist');
|
cy.get('#searchHints').find('.search-hint__search-type-selector button > .icon-file-text-outline').should('not.exist');
|
||||||
|
|
||||||
// # Search for posts
|
// # Search for posts
|
||||||
cy.uiGetSearchBox().first().type('sample').type('{enter}');
|
cy.uiGetSearchBox().type('sample').type('{enter}');
|
||||||
|
|
||||||
// * Verify search results do not have File button
|
// * Verify search results do not have File button
|
||||||
cy.get('.files-tab').should('not.exist');
|
cy.get('.files-tab').should('not.exist');
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ describe('Keyboard Shortcuts', () => {
|
|||||||
it('MM-T1250 CTRL/CMD+SHIFT+L - Set focus to center channel message box (with SEARCH RHS open)', () => {
|
it('MM-T1250 CTRL/CMD+SHIFT+L - Set focus to center channel message box (with SEARCH RHS open)', () => {
|
||||||
// # Search
|
// # Search
|
||||||
cy.uiGetSearchContainer().click();
|
cy.uiGetSearchContainer().click();
|
||||||
cy.uiGetSearchBox().first().type('test{enter}');
|
cy.uiGetSearchBox().type('test{enter}');
|
||||||
|
|
||||||
// * Wait for the RHS to open and the search results to appear
|
// * Wait for the RHS to open and the search results to appear
|
||||||
cy.contains('.sidebar--right__header', 'Search Results').should('be.visible');
|
cy.contains('.sidebar--right__header', 'Search Results').should('be.visible');
|
||||||
|
|||||||
@@ -720,7 +720,7 @@ describe('Messaging', () => {
|
|||||||
cy.getLastPostId().then((postId) => {
|
cy.getLastPostId().then((postId) => {
|
||||||
// # Search for the posted message
|
// # Search for the posted message
|
||||||
cy.uiGetSearchContainer().click();
|
cy.uiGetSearchContainer().click();
|
||||||
cy.uiGetSearchBox().should('be.visible').first().type(messageX).type('{enter}').wait(TIMEOUTS.HALF_SEC);
|
cy.uiGetSearchBox().should('be.visible').type(messageX).type('{enter}').wait(TIMEOUTS.HALF_SEC);
|
||||||
|
|
||||||
// # Click on post dot menu so we can edit
|
// # Click on post dot menu so we can edit
|
||||||
cy.clickPostDotMenu(postId, 'SEARCH');
|
cy.clickPostDotMenu(postId, 'SEARCH');
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ describe('Message permalink', () => {
|
|||||||
|
|
||||||
// # Search for a message in the current channel
|
// # Search for a message in the current channel
|
||||||
cy.uiGetSearchContainer().click();
|
cy.uiGetSearchContainer().click();
|
||||||
cy.uiGetSearchBox().first().clear().type('in:town-square').wait(TIMEOUTS.HALF_SEC).type('{enter}{enter}');
|
cy.uiGetSearchBox().clear().type('in:town-square').wait(TIMEOUTS.HALF_SEC).type('{enter}{enter}');
|
||||||
|
|
||||||
// # Jump to first permalink view (most recent message)
|
// # Jump to first permalink view (most recent message)
|
||||||
cy.get('.search-item__jump').first().click();
|
cy.get('.search-item__jump').first().click();
|
||||||
@@ -73,7 +73,7 @@ describe('Message permalink', () => {
|
|||||||
|
|
||||||
// # Search for a message in the current channel
|
// # Search for a message in the current channel
|
||||||
cy.uiGetSearchContainer().click();
|
cy.uiGetSearchContainer().click();
|
||||||
cy.uiGetSearchBox().first().clear().type('in:town-square').wait(TIMEOUTS.HALF_SEC).type('{enter}{enter}');
|
cy.uiGetSearchBox().clear().type('in:town-square').wait(TIMEOUTS.HALF_SEC).type('{enter}{enter}');
|
||||||
|
|
||||||
// # Jump to first permalink view (most recent message)
|
// # Jump to first permalink view (most recent message)
|
||||||
cy.get('.search-item__jump').first().click();
|
cy.get('.search-item__jump').first().click();
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ describe('Permalink message edit', () => {
|
|||||||
|
|
||||||
// # Find searchWord and verify edited post
|
// # Find searchWord and verify edited post
|
||||||
cy.uiGetSearchContainer().click();
|
cy.uiGetSearchContainer().click();
|
||||||
cy.uiGetSearchBox().should('be.visible').first().type(searchWord).type('{enter}');
|
cy.uiGetSearchBox().should('be.visible').type(searchWord).type('{enter}');
|
||||||
cy.get('.search-item__jump').first().click();
|
cy.get('.search-item__jump').first().click();
|
||||||
|
|
||||||
// # Check if url include the permalink
|
// # Check if url include the permalink
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ describe('Post PreHeader', () => {
|
|||||||
|
|
||||||
// # Search for the channel.
|
// # Search for the channel.
|
||||||
cy.uiGetSearchContainer().click();
|
cy.uiGetSearchContainer().click();
|
||||||
cy.uiGetSearchBox().first().type('test both pinned and saved {enter}');
|
cy.uiGetSearchBox().type('test both pinned and saved {enter}');
|
||||||
|
|
||||||
// * Check that the post pre-header has both pinned and saved links in RHS search results
|
// * Check that the post pre-header has both pinned and saved links in RHS search results
|
||||||
cy.get('#searchContainer').should('be.visible').within(() => {
|
cy.get('#searchContainer').should('be.visible').within(() => {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ describe('Search', () => {
|
|||||||
cy.uiGetSearchContainer().click();
|
cy.uiGetSearchContainer().click();
|
||||||
|
|
||||||
// # Write something on the input
|
// # Write something on the input
|
||||||
cy.uiGetSearchBox().first().clear().wait(TIMEOUTS.HALF_SEC).type('abc').wait(TIMEOUTS.HALF_SEC).type('{enter}');
|
cy.uiGetSearchBox().clear().wait(TIMEOUTS.HALF_SEC).type('abc').wait(TIMEOUTS.HALF_SEC).type('{enter}');
|
||||||
|
|
||||||
cy.uiGetSearchContainer().click();
|
cy.uiGetSearchContainer().click();
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ describe('Search', () => {
|
|||||||
// * Verify search input field exists and not search button, as inputs contains placeholder not buttons/icons
|
// * Verify search input field exists and not search button, as inputs contains placeholder not buttons/icons
|
||||||
// and then type in a search text
|
// and then type in a search text
|
||||||
cy.uiGetSearchContainer().click();
|
cy.uiGetSearchContainer().click();
|
||||||
cy.uiGetSearchBox().first().clear().wait(TIMEOUTS.HALF_SEC).type(searchText + '{enter}').wait(TIMEOUTS.HALF_SEC);
|
cy.uiGetSearchBox().clear().wait(TIMEOUTS.HALF_SEC).type(searchText + '{enter}').wait(TIMEOUTS.HALF_SEC);
|
||||||
|
|
||||||
// # Now click on the saved post button from the header
|
// # Now click on the saved post button from the header
|
||||||
cy.uiGetSavedPostButton().click();
|
cy.uiGetSavedPostButton().click();
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ describe('Search', () => {
|
|||||||
// # Click on "x" displayed on searchbox
|
// # Click on "x" displayed on searchbox
|
||||||
cy.uiGetSearchBox().parent().siblings('.input-clear-x').wait(TIMEOUTS.ONE_SEC).click({force: true});
|
cy.uiGetSearchBox().parent().siblings('.input-clear-x').wait(TIMEOUTS.ONE_SEC).click({force: true});
|
||||||
cy.uiGetSearchBox().parents('[class*="SearchInputContainer"]').siblings('#searchHints').should('be.visible');
|
cy.uiGetSearchBox().parents('[class*="SearchInputContainer"]').siblings('#searchHints').should('be.visible');
|
||||||
cy.uiGetSearchBox().first().focus().type('{esc}');
|
cy.uiGetSearchBox().focus().type('{esc}');
|
||||||
|
|
||||||
// # RHS should be visible with search results
|
// # RHS should be visible with search results
|
||||||
cy.get('#search-items-container').should('be.visible');
|
cy.get('#search-items-container').should('be.visible');
|
||||||
@@ -134,7 +134,7 @@ describe('Search', () => {
|
|||||||
assertSearchHint();
|
assertSearchHint();
|
||||||
|
|
||||||
// # Clear search box
|
// # Clear search box
|
||||||
cy.uiGetSearchBox().get('.input-clear-x').first().click({force: true}).wait(TIMEOUTS.HALF_SEC);
|
cy.uiGetSearchBox().get('.input-clear-x').click({force: true}).wait(TIMEOUTS.HALF_SEC);
|
||||||
|
|
||||||
// # Search for search term in:town-square{enter}
|
// # Search for search term in:town-square{enter}
|
||||||
cy.uiGetSearchBox().type('in:town-square').wait(TIMEOUTS.HALF_SEC);
|
cy.uiGetSearchBox().type('in:town-square').wait(TIMEOUTS.HALF_SEC);
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ describe('Search', () => {
|
|||||||
|
|
||||||
// # Search for "apple"
|
// # Search for "apple"
|
||||||
cy.uiGetSearchContainer().click();
|
cy.uiGetSearchContainer().click();
|
||||||
cy.uiGetSearchBox().should('be.visible').first().type(apple).type('{enter}');
|
cy.uiGetSearchBox().should('be.visible').type(apple).type('{enter}');
|
||||||
|
|
||||||
// # Get last postId
|
// # Get last postId
|
||||||
cy.getLastPostId().as('lastPostId');
|
cy.getLastPostId().as('lastPostId');
|
||||||
@@ -50,7 +50,7 @@ describe('Search', () => {
|
|||||||
cy.uiGetSearchContainer().click();
|
cy.uiGetSearchContainer().click();
|
||||||
|
|
||||||
// * Type banana on search box but don't hit search
|
// * Type banana on search box but don't hit search
|
||||||
cy.uiGetSearchBox().first().clear({force: true}).type(banana, {force: true});
|
cy.uiGetSearchBox().clear({force: true}).type(banana, {force: true});
|
||||||
|
|
||||||
// * Search result should not change and remain as one result with highlight still on apple
|
// * Search result should not change and remain as one result with highlight still on apple
|
||||||
cy.get('@lastPostId').then((postId) => {
|
cy.get('@lastPostId').then((postId) => {
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ describe('Search', () => {
|
|||||||
|
|
||||||
//# Search for the message
|
//# Search for the message
|
||||||
cy.uiGetSearchContainer().should('be.visible').click();
|
cy.uiGetSearchContainer().should('be.visible').click();
|
||||||
cy.uiGetSearchBox().first().clear().type(`${message}{enter}`);
|
cy.uiGetSearchBox().clear().type(`${message}{enter}`);
|
||||||
|
|
||||||
// * Should return exactly one result from the group channel and matches the message
|
// * 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(() => {
|
cy.findAllByTestId('search-item-container').should('be.visible').and('have.length', 1).within(() => {
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ describe('Search Date Filter', () => {
|
|||||||
|
|
||||||
// # Type before: in search field
|
// # Type before: in search field
|
||||||
cy.uiGetSearchContainer().should('be.visible').click();
|
cy.uiGetSearchContainer().should('be.visible').click();
|
||||||
cy.uiGetSearchBox().first().clear().type('before:');
|
cy.uiGetSearchBox().clear().type('before:');
|
||||||
|
|
||||||
// * Day picker should be visible
|
// * Day picker should be visible
|
||||||
cy.get('.rdp').
|
cy.get('.rdp').
|
||||||
@@ -74,7 +74,7 @@ describe('Search Date Filter', () => {
|
|||||||
cy.uiGetSearchBox().parent('.input-wrapper').siblings('.input-clear-x').click({force: true});
|
cy.uiGetSearchBox().parent('.input-wrapper').siblings('.input-clear-x').click({force: true});
|
||||||
|
|
||||||
// * The "x" to clear the search query has disappeared
|
// * The "x" to clear the search query has disappeared
|
||||||
cy.uiGetSearchBox().first().should('have.value', '');
|
cy.uiGetSearchBox().should('have.value', '');
|
||||||
|
|
||||||
cy.uiGetSearchContainer().should('be.visible').click();
|
cy.uiGetSearchContainer().should('be.visible').click();
|
||||||
});
|
});
|
||||||
@@ -84,7 +84,7 @@ describe('Search Date Filter', () => {
|
|||||||
|
|
||||||
// # Type before: in search field
|
// # Type before: in search field
|
||||||
cy.uiGetSearchContainer().should('be.visible').click();
|
cy.uiGetSearchContainer().should('be.visible').click();
|
||||||
cy.uiGetSearchBox().first().clear().type('before:');
|
cy.uiGetSearchBox().clear().type('before:');
|
||||||
|
|
||||||
// * Date picker should be visible
|
// * Date picker should be visible
|
||||||
cy.get('.rdp').
|
cy.get('.rdp').
|
||||||
@@ -135,7 +135,7 @@ describe('Search Date Filter', () => {
|
|||||||
should('have.value', '');
|
should('have.value', '');
|
||||||
|
|
||||||
// # Enter query to search box and then click "x" to the right of the search term
|
// # Enter query to search box and then click "x" to the right of the search term
|
||||||
cy.uiGetSearchBox().first().clear().wait(TIMEOUTS.HALF_SEC).type(queryString);
|
cy.uiGetSearchBox().clear().wait(TIMEOUTS.HALF_SEC).type(queryString);
|
||||||
cy.uiGetSearchBox().parent('.input-wrapper').siblings('.input-clear-x').click({force: true});
|
cy.uiGetSearchBox().parent('.input-wrapper').siblings('.input-clear-x').click({force: true});
|
||||||
|
|
||||||
// * The "x" to clear the search query has disappeared
|
// * The "x" to clear the search query has disappeared
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ describe('Negative search filters will omit results', () => {
|
|||||||
function search(query) {
|
function search(query) {
|
||||||
cy.reload();
|
cy.reload();
|
||||||
cy.uiGetSearchContainer().should('be.visible').click();
|
cy.uiGetSearchContainer().should('be.visible').click();
|
||||||
cy.uiGetSearchBox().first().clear().wait(TIMEOUTS.HALF_SEC).type(query).wait(TIMEOUTS.HALF_SEC).type('{enter}');
|
cy.uiGetSearchBox().clear().wait(TIMEOUTS.HALF_SEC).type(query).wait(TIMEOUTS.HALF_SEC).type('{enter}');
|
||||||
|
|
||||||
cy.get('#loadingSpinner').should('not.exist');
|
cy.get('#loadingSpinner').should('not.exist');
|
||||||
cy.uiGetRHSSearchContainer();
|
cy.uiGetRHSSearchContainer();
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ Cypress.Commands.add('uiGetSearchContainer', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Cypress.Commands.add('uiGetSearchBox', () => {
|
Cypress.Commands.add('uiGetSearchBox', () => {
|
||||||
return cy.get('.search-bar').should('be.visible');
|
return cy.findByRole('searchbox').should('be.visible');
|
||||||
});
|
});
|
||||||
|
|
||||||
Cypress.Commands.add('uiGetRecentMentionButton', () => {
|
Cypress.Commands.add('uiGetRecentMentionButton', () => {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export {
|
|||||||
export {
|
export {
|
||||||
components,
|
components,
|
||||||
GlobalHeader,
|
GlobalHeader,
|
||||||
SearchPopover,
|
SearchBox,
|
||||||
ChannelsCenterView,
|
ChannelsCenterView,
|
||||||
ChannelsSidebarLeft,
|
ChannelsSidebarLeft,
|
||||||
ChannelsSidebarRight,
|
ChannelsSidebarRight,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
import {Locator, expect} from '@playwright/test';
|
import {Locator, expect} from '@playwright/test';
|
||||||
|
|
||||||
export default class SearchPopover {
|
export default class SearchBox {
|
||||||
readonly container: Locator;
|
readonly container: Locator;
|
||||||
|
|
||||||
readonly messagesButton;
|
readonly messagesButton;
|
||||||
@@ -30,6 +30,7 @@ export default class SearchPopover {
|
|||||||
async clearIfPossible() {
|
async clearIfPossible() {
|
||||||
if (await this.clearButton.isVisible()) {
|
if (await this.clearButton.isVisible()) {
|
||||||
await this.clearButton.click();
|
await this.clearButton.click();
|
||||||
|
await expect(this.searchInput).toHaveValue('');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -13,7 +13,7 @@ import FindChannelsModal from './channels/find_channels_modal';
|
|||||||
import SettingsModal from './channels/settings/settings_modal';
|
import SettingsModal from './channels/settings/settings_modal';
|
||||||
import Footer from './footer';
|
import Footer from './footer';
|
||||||
import GlobalHeader from './global_header';
|
import GlobalHeader from './global_header';
|
||||||
import SearchPopover from './channels/search_popover';
|
import SearchBox from './channels/search_box';
|
||||||
import MainHeader from './main_header';
|
import MainHeader from './main_header';
|
||||||
import PostDotMenu from './channels/post_dot_menu';
|
import PostDotMenu from './channels/post_dot_menu';
|
||||||
import PostReminderMenu from './channels/post_reminder_menu';
|
import PostReminderMenu from './channels/post_reminder_menu';
|
||||||
@@ -47,7 +47,7 @@ import DraftPost from './channels/draft_post';
|
|||||||
|
|
||||||
const components = {
|
const components = {
|
||||||
GlobalHeader,
|
GlobalHeader,
|
||||||
SearchPopover,
|
SearchBox,
|
||||||
ChannelsCenterView,
|
ChannelsCenterView,
|
||||||
ChannelsSidebarLeft,
|
ChannelsSidebarLeft,
|
||||||
ChannelsSidebarRight,
|
ChannelsSidebarRight,
|
||||||
@@ -94,7 +94,7 @@ const components = {
|
|||||||
export {
|
export {
|
||||||
components,
|
components,
|
||||||
GlobalHeader,
|
GlobalHeader,
|
||||||
SearchPopover,
|
SearchBox,
|
||||||
ChannelsCenterView,
|
ChannelsCenterView,
|
||||||
ChannelsSidebarLeft,
|
ChannelsSidebarLeft,
|
||||||
ChannelsSidebarRight,
|
ChannelsSidebarRight,
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export default class ChannelsPage {
|
|||||||
|
|
||||||
readonly globalHeader;
|
readonly globalHeader;
|
||||||
readonly userAccountMenuButton;
|
readonly userAccountMenuButton;
|
||||||
readonly searchPopover;
|
readonly searchBox;
|
||||||
readonly centerView;
|
readonly centerView;
|
||||||
readonly scheduledDraftModal;
|
readonly scheduledDraftModal;
|
||||||
readonly sidebarLeft;
|
readonly sidebarLeft;
|
||||||
@@ -40,7 +40,7 @@ export default class ChannelsPage {
|
|||||||
|
|
||||||
// The main areas of the app
|
// The main areas of the app
|
||||||
this.globalHeader = new components.GlobalHeader(this, page.locator('#global-header'));
|
this.globalHeader = new components.GlobalHeader(this, page.locator('#global-header'));
|
||||||
this.searchPopover = new components.SearchPopover(page.locator('#searchPopover'));
|
this.searchBox = new components.SearchBox(page.locator('#searchBox'));
|
||||||
this.centerView = new components.ChannelsCenterView(page.getByTestId('channel_view'), page);
|
this.centerView = new components.ChannelsCenterView(page.getByTestId('channel_view'), page);
|
||||||
this.sidebarLeft = new components.ChannelsSidebarLeft(page.locator('#SidebarContainer'));
|
this.sidebarLeft = new components.ChannelsSidebarLeft(page.locator('#SidebarContainer'));
|
||||||
this.sidebarRight = new components.ChannelsSidebarRight(page.locator('#sidebar-right'));
|
this.sidebarRight = new components.ChannelsSidebarRight(page.locator('#sidebar-right'));
|
||||||
|
|||||||
@@ -19,14 +19,14 @@ test('MM-64155 search box clear button should not leave type badge after closing
|
|||||||
|
|
||||||
// # Type something in the search box
|
// # Type something in the search box
|
||||||
const searchText = 'abcdef';
|
const searchText = 'abcdef';
|
||||||
const {searchInput} = channelsPage.searchPopover;
|
const {searchInput} = channelsPage.searchBox;
|
||||||
await searchInput.pressSequentially(searchText);
|
await searchInput.pressSequentially(searchText);
|
||||||
|
|
||||||
// * Verify text was entered
|
// * Verify text was entered
|
||||||
await expect(searchInput).toHaveValue(searchText);
|
await expect(searchInput).toHaveValue(searchText);
|
||||||
|
|
||||||
// # Click the clear button
|
// # Click the clear button
|
||||||
await channelsPage.searchPopover.clearIfPossible();
|
await channelsPage.searchBox.clearIfPossible();
|
||||||
|
|
||||||
// * Verify the input is cleared
|
// * Verify the input is cleared
|
||||||
await expect(searchInput).toHaveValue('');
|
await expect(searchInput).toHaveValue('');
|
||||||
@@ -35,7 +35,7 @@ test('MM-64155 search box clear button should not leave type badge after closing
|
|||||||
await channelsPage.page.click('body', {position: {x: 0, y: 0}});
|
await channelsPage.page.click('body', {position: {x: 0, y: 0}});
|
||||||
|
|
||||||
// * Verify the search box is closed
|
// * Verify the search box is closed
|
||||||
await expect(channelsPage.searchPopover.container).not.toBeVisible();
|
await expect(channelsPage.searchBox.container).not.toBeVisible();
|
||||||
|
|
||||||
// * Verify there is no search type badge/chip in the search bar
|
// * Verify there is no search type badge/chip in the search bar
|
||||||
// The search type badge is rendered when searchType is either 'messages' or 'files'
|
// The search type badge is rendered when searchType is either 'messages' or 'files'
|
||||||
|
|||||||
@@ -22,12 +22,12 @@ test('Search box suggestion must be case insensitive', async ({pw}) => {
|
|||||||
|
|
||||||
// Should work as expected when using lowercase
|
// Should work as expected when using lowercase
|
||||||
// # Type in lowercase "off" to search for the "Off-Topic" channel
|
// # Type in lowercase "off" to search for the "Off-Topic" channel
|
||||||
const {searchInput} = channelsPage.searchPopover;
|
const {searchInput} = channelsPage.searchBox;
|
||||||
await searchInput.pressSequentially(`In:${searchWord}`);
|
await searchInput.pressSequentially(`In:${searchWord}`);
|
||||||
|
|
||||||
// * The suggestion should be visible
|
// * The suggestion should be visible
|
||||||
await expect(channelsPage.searchPopover.selectedSuggestion).toBeVisible();
|
await expect(channelsPage.searchBox.selectedSuggestion).toBeVisible();
|
||||||
await expect(channelsPage.searchPopover.selectedSuggestion).toHaveText(channelName);
|
await expect(channelsPage.searchBox.selectedSuggestion).toHaveText(channelName);
|
||||||
|
|
||||||
// # Press Enter to select the suggestion and another Enter to search
|
// # Press Enter to select the suggestion and another Enter to search
|
||||||
await searchInput.press('Enter');
|
await searchInput.press('Enter');
|
||||||
@@ -41,14 +41,14 @@ test('Search box suggestion must be case insensitive', async ({pw}) => {
|
|||||||
await channelsPage.globalHeader.openSearch();
|
await channelsPage.globalHeader.openSearch();
|
||||||
|
|
||||||
// # Clear its content
|
// # Clear its content
|
||||||
await channelsPage.searchPopover.clearIfPossible();
|
await channelsPage.searchBox.clearIfPossible();
|
||||||
|
|
||||||
// # Type in uppercase "OFF" to search for the "Off-Topic" channel
|
// # Type in uppercase "OFF" to search for the "Off-Topic" channel
|
||||||
await searchInput.pressSequentially(`In:${searchWord.toUpperCase()}`);
|
await searchInput.type(`In:${searchWord.toUpperCase()}`);
|
||||||
|
|
||||||
// * The suggestion should be visible
|
// * The suggestion should be visible
|
||||||
await expect(channelsPage.searchPopover.selectedSuggestion).toBeVisible();
|
await expect(channelsPage.searchBox.selectedSuggestion).toBeVisible();
|
||||||
await expect(channelsPage.searchPopover.selectedSuggestion).toHaveText(channelName);
|
await expect(channelsPage.searchBox.selectedSuggestion).toHaveText(channelName);
|
||||||
|
|
||||||
// # Press Enter to select the suggestion and another Enter to search
|
// # Press Enter to select the suggestion and another Enter to search
|
||||||
await searchInput.press('Enter');
|
await searchInput.press('Enter');
|
||||||
@@ -73,12 +73,12 @@ test('remove extra whitespace when selecting a user', async ({pw}) => {
|
|||||||
await channelsPage.globalHeader.openSearch();
|
await channelsPage.globalHeader.openSearch();
|
||||||
|
|
||||||
// # Type "from:" followed by multiple spaces
|
// # Type "from:" followed by multiple spaces
|
||||||
const {searchInput} = channelsPage.searchPopover;
|
const {searchInput} = channelsPage.searchBox;
|
||||||
await searchInput.pressSequentially(`from: ${admin.username}`);
|
await searchInput.pressSequentially(`from: ${admin.username}`);
|
||||||
|
|
||||||
// * The suggestion should be visible
|
// * The suggestion should be visible
|
||||||
await expect(channelsPage.searchPopover.selectedSuggestion).toBeVisible();
|
await expect(channelsPage.searchBox.selectedSuggestion).toBeVisible();
|
||||||
await expect(channelsPage.searchPopover.selectedSuggestion).toHaveText(`@` + admin.username);
|
await expect(channelsPage.searchBox.selectedSuggestion).toHaveText(`@` + admin.username);
|
||||||
|
|
||||||
// # Press enter to validate the selection
|
// # Press enter to validate the selection
|
||||||
await searchInput.press('Enter');
|
await searchInput.press('Enter');
|
||||||
|
|||||||
@@ -1,8 +1,19 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import {
|
||||||
|
useFloating,
|
||||||
|
autoUpdate,
|
||||||
|
useClick,
|
||||||
|
useDismiss,
|
||||||
|
useInteractions,
|
||||||
|
useRole,
|
||||||
|
FloatingFocusManager,
|
||||||
|
FloatingPortal,
|
||||||
|
offset,
|
||||||
|
} from '@floating-ui/react';
|
||||||
import React, {useEffect, useState, useRef, useCallback} from 'react';
|
import React, {useEffect, useState, useRef, useCallback} from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage, useIntl} from 'react-intl';
|
||||||
import {useSelector, useDispatch} from 'react-redux';
|
import {useSelector, useDispatch} from 'react-redux';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
@@ -16,30 +27,15 @@ import {updateSearchTerms, showSearchResults, updateSearchType, updateSearchTeam
|
|||||||
import {getSearchButtons} from 'selectors/plugins';
|
import {getSearchButtons} from 'selectors/plugins';
|
||||||
import {getSearchTeam, getSearchTerms, getSearchType} from 'selectors/rhs';
|
import {getSearchTeam, getSearchTerms, getSearchType} from 'selectors/rhs';
|
||||||
|
|
||||||
import Popover from 'components/widgets/popover';
|
|
||||||
|
|
||||||
import a11yController from 'utils/a11y_controller_instance';
|
import a11yController from 'utils/a11y_controller_instance';
|
||||||
import {focusElement} from 'utils/a11y_utils';
|
import {focusElement} from 'utils/a11y_utils';
|
||||||
import Constants from 'utils/constants';
|
import {RootHtmlPortalId, Constants} from 'utils/constants';
|
||||||
import * as Keyboard from 'utils/keyboard';
|
import * as Keyboard from 'utils/keyboard';
|
||||||
import {isServerVersionGreaterThanOrEqualTo} from 'utils/server_version';
|
import {isServerVersionGreaterThanOrEqualTo} from 'utils/server_version';
|
||||||
import {isDesktopApp, getDesktopVersion, isMacApp} from 'utils/user_agent';
|
import {isDesktopApp, getDesktopVersion, isMacApp} from 'utils/user_agent';
|
||||||
|
|
||||||
import SearchBox from './search_box';
|
import SearchBox from './search_box';
|
||||||
|
|
||||||
const PopoverStyled = styled(Popover)`
|
|
||||||
min-width: 600px;
|
|
||||||
left: -90px;
|
|
||||||
top: -12px;
|
|
||||||
border-radius: 12px;
|
|
||||||
max-height: 90vh;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
.popover-content {
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const SearchTypeBadge = styled.div`
|
const SearchTypeBadge = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -106,7 +102,18 @@ const NewSearchTerms = styled.span`
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const SearchBoxContainer = styled.div`
|
||||||
|
min-width: 600px;
|
||||||
|
border-radius: 12px;
|
||||||
|
max-height: 90vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: var(--center-channel-bg);
|
||||||
|
box-shadow: 0 0 0 1px rgba(var(--center-channel-color-rgb), 0.16), 0 4px 6px rgba(0, 0, 0, 0.12);
|
||||||
|
z-index: 1050;
|
||||||
|
`;
|
||||||
|
|
||||||
const NewSearch = (): JSX.Element => {
|
const NewSearch = (): JSX.Element => {
|
||||||
|
const intl = useIntl();
|
||||||
const currentChannelName = useSelector(getCurrentChannelNameForSearchShortcut);
|
const currentChannelName = useSelector(getCurrentChannelNameForSearchShortcut);
|
||||||
const searchTerms = useSelector(getSearchTerms) || '';
|
const searchTerms = useSelector(getSearchTerms) || '';
|
||||||
const searchType = useSelector(getSearchType) || '';
|
const searchType = useSelector(getSearchType) || '';
|
||||||
@@ -120,7 +127,25 @@ const NewSearch = (): JSX.Element => {
|
|||||||
const [focused, setFocused] = useState<boolean>(false);
|
const [focused, setFocused] = useState<boolean>(false);
|
||||||
const [currentChannel, setCurrentChannel] = useState('');
|
const [currentChannel, setCurrentChannel] = useState('');
|
||||||
const searchBoxRef = useRef<HTMLDivElement | null>(null);
|
const searchBoxRef = useRef<HTMLDivElement | null>(null);
|
||||||
const searchButtonRef = useRef<HTMLDivElement | null>(null);
|
|
||||||
|
const {refs, floatingStyles, context: floatingContext} = useFloating<HTMLDivElement>({
|
||||||
|
open: focused,
|
||||||
|
onOpenChange: setFocused,
|
||||||
|
whileElementsMounted: autoUpdate,
|
||||||
|
placement: 'bottom',
|
||||||
|
middleware: [offset({mainAxis: -28})],
|
||||||
|
});
|
||||||
|
const searchButtonRef = refs.reference as React.RefObject<HTMLDivElement>;
|
||||||
|
|
||||||
|
const clickInteractions = useClick(floatingContext);
|
||||||
|
const dismissInteraction = useDismiss(floatingContext);
|
||||||
|
const role = useRole(floatingContext);
|
||||||
|
|
||||||
|
const {getReferenceProps, getFloatingProps} = useInteractions([
|
||||||
|
clickInteractions,
|
||||||
|
dismissInteraction,
|
||||||
|
role,
|
||||||
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const isDesktop = isDesktopApp() && isServerVersionGreaterThanOrEqualTo(getDesktopVersion(), '4.7.0');
|
const isDesktop = isDesktopApp() && isServerVersionGreaterThanOrEqualTo(getDesktopVersion(), '4.7.0');
|
||||||
@@ -255,75 +280,89 @@ const NewSearch = (): JSX.Element => {
|
|||||||
const clearSearchType = useCallback(() => dispatch(updateSearchType('')), []);
|
const clearSearchType = useCallback(() => dispatch(updateSearchType('')), []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NewSearchContainer
|
<>
|
||||||
tabIndex={0}
|
<NewSearchContainer
|
||||||
onKeyDown={openSearchBoxOnKeyPress}
|
tabIndex={0}
|
||||||
onClick={openSearchBox}
|
id='searchFormContainer'
|
||||||
id='searchFormContainer'
|
role='button'
|
||||||
role='button'
|
className='a11y__region'
|
||||||
className='a11y__region'
|
ref={refs.setReference}
|
||||||
ref={searchButtonRef}
|
{...getReferenceProps({
|
||||||
>
|
onKeyDown: openSearchBoxOnKeyPress,
|
||||||
<i className='icon icon-magnify'/>
|
onClick: openSearchBox,
|
||||||
{(searchType === 'messages' || searchType === 'files') && (
|
})}
|
||||||
<SearchTypeBadge data-testid='searchTypeBadge'>
|
>
|
||||||
{searchType === 'messages' && (
|
<i className='icon icon-magnify'/>
|
||||||
<FormattedMessage
|
{(searchType === 'messages' || searchType === 'files') && (
|
||||||
id='search_bar.search_types.messages'
|
<SearchTypeBadge data-testid='searchTypeBadge'>
|
||||||
defaultMessage='MESSAGES'
|
{searchType === 'messages' && (
|
||||||
|
<FormattedMessage
|
||||||
|
id='search_bar.search_types.messages'
|
||||||
|
defaultMessage='MESSAGES'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{searchType === 'files' && (
|
||||||
|
<FormattedMessage
|
||||||
|
id='search_bar.search_types.files'
|
||||||
|
defaultMessage='FILES'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<i
|
||||||
|
className='icon icon-close icon-12'
|
||||||
|
onClick={clearSearchType}
|
||||||
/>
|
/>
|
||||||
)}
|
</SearchTypeBadge>
|
||||||
{searchType === 'files' && (
|
)}
|
||||||
<FormattedMessage
|
{searchTerms && <NewSearchTerms tabIndex={0}>{searchTerms}</NewSearchTerms>}
|
||||||
id='search_bar.search_types.files'
|
{searchTerms && (
|
||||||
defaultMessage='FILES'
|
<CloseIcon
|
||||||
/>
|
data-testid='input-clear'
|
||||||
)}
|
role='button'
|
||||||
<i
|
onClick={onClose}
|
||||||
className='icon icon-close icon-12'
|
|
||||||
onClick={clearSearchType}
|
|
||||||
/>
|
|
||||||
</SearchTypeBadge>
|
|
||||||
)}
|
|
||||||
{searchTerms && <NewSearchTerms tabIndex={0}>{searchTerms}</NewSearchTerms>}
|
|
||||||
{searchTerms && (
|
|
||||||
<CloseIcon
|
|
||||||
data-testid='input-clear'
|
|
||||||
role='button'
|
|
||||||
onClick={onClose}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className='input-clear-x'
|
|
||||||
aria-hidden='true'
|
|
||||||
>
|
>
|
||||||
<i className='icon icon-close-circle'/>
|
<span
|
||||||
</span>
|
className='input-clear-x'
|
||||||
</CloseIcon>
|
aria-hidden='true'
|
||||||
)}
|
>
|
||||||
{!searchTerms && (
|
<i className='icon icon-close-circle'/>
|
||||||
<FormattedMessage
|
</span>
|
||||||
id='search_bar.search'
|
</CloseIcon>
|
||||||
defaultMessage='Search'
|
)}
|
||||||
/>
|
{!searchTerms && (
|
||||||
)}
|
<FormattedMessage
|
||||||
{focused && (
|
id='search_bar.search'
|
||||||
<PopoverStyled
|
defaultMessage='Search'
|
||||||
id='searchPopover'
|
|
||||||
placement='bottom'
|
|
||||||
>
|
|
||||||
<SearchBox
|
|
||||||
ref={searchBoxRef}
|
|
||||||
onClose={closeSearchBox}
|
|
||||||
onSearch={runSearch}
|
|
||||||
initialSearchTerms={currentChannel ? `in:${currentChannel} ` : searchTerms}
|
|
||||||
initialSearchType={searchType}
|
|
||||||
initialSearchTeam={searchTeam}
|
|
||||||
crossTeamSearchEnabled={crossTeamSearchEnabled}
|
|
||||||
myTeams={myTeams}
|
|
||||||
/>
|
/>
|
||||||
</PopoverStyled>
|
)}
|
||||||
|
</NewSearchContainer>
|
||||||
|
|
||||||
|
{focused && (
|
||||||
|
<FloatingPortal id={RootHtmlPortalId}>
|
||||||
|
<FloatingFocusManager context={floatingContext}>
|
||||||
|
<SearchBoxContainer
|
||||||
|
ref={refs.setFloating}
|
||||||
|
style={floatingStyles}
|
||||||
|
{...getFloatingProps()}
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id: 'search_bar.search_box',
|
||||||
|
defaultMessage: 'Search box',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<SearchBox
|
||||||
|
ref={searchBoxRef}
|
||||||
|
onClose={closeSearchBox}
|
||||||
|
onSearch={runSearch}
|
||||||
|
initialSearchTerms={currentChannel ? `in:${currentChannel} ` : searchTerms}
|
||||||
|
initialSearchType={searchType}
|
||||||
|
initialSearchTeam={searchTeam}
|
||||||
|
crossTeamSearchEnabled={crossTeamSearchEnabled}
|
||||||
|
myTeams={myTeams}
|
||||||
|
/>
|
||||||
|
</SearchBoxContainer>
|
||||||
|
</FloatingFocusManager>
|
||||||
|
</FloatingPortal>
|
||||||
)}
|
)}
|
||||||
</NewSearchContainer>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -268,12 +268,6 @@ const SearchBox = forwardRef(
|
|||||||
<SearchBoxContainer
|
<SearchBoxContainer
|
||||||
ref={ref}
|
ref={ref}
|
||||||
id='searchBox'
|
id='searchBox'
|
||||||
aria-label={intl.formatMessage({
|
|
||||||
id: 'search_bar.search',
|
|
||||||
defaultMessage: 'Search',
|
|
||||||
})}
|
|
||||||
aria-describedby='searchHints'
|
|
||||||
role='searchbox'
|
|
||||||
>
|
>
|
||||||
<CloseIcon
|
<CloseIcon
|
||||||
data-testid='searchBoxClose'
|
data-testid='searchBoxClose'
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ const SearchInput = forwardRef<HTMLInputElement, Props>(({searchTerms, searchTyp
|
|||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
onKeyDown={onKeyDown}
|
onKeyDown={onKeyDown}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
|
role='searchbox'
|
||||||
/>
|
/>
|
||||||
{searchTerms.length > 0 && (
|
{searchTerms.length > 0 && (
|
||||||
<ClearButton
|
<ClearButton
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React, {useCallback} from 'react';
|
import React, {useCallback} from 'react';
|
||||||
|
import {FormattedMessage} from 'react-intl';
|
||||||
import {useSelector} from 'react-redux';
|
import {useSelector} from 'react-redux';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
@@ -93,7 +94,15 @@ const SearchSuggestions = ({searchType, searchTeam, searchTerms, suggestionsHead
|
|||||||
className='sr-only'
|
className='sr-only'
|
||||||
key={providerResults.terms[selectedOption]}
|
key={providerResults.terms[selectedOption]}
|
||||||
>
|
>
|
||||||
{generateLabel(providerResults.items[selectedOption])}
|
<FormattedMessage
|
||||||
|
id='search_box_suggestions.suggestions_readout'
|
||||||
|
defaultMessage='{label} ({idx} of {total} results available)'
|
||||||
|
values={{
|
||||||
|
label: generateLabel(providerResults.items[selectedOption]),
|
||||||
|
idx: selectedOption + 1,
|
||||||
|
total: providerResults.items.length,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{providerResults.items.map((item, idx) => {
|
{providerResults.items.map((item, idx) => {
|
||||||
|
|||||||
@@ -55,10 +55,13 @@ const SearchTypeSelector = ({searchType, setSearchType}: Props) => {
|
|||||||
const searchPluginButtons = useSelector(getSearchButtons);
|
const searchPluginButtons = useSelector(getSearchButtons);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SearchTypeSelectorContainer>
|
<SearchTypeSelectorContainer
|
||||||
|
role='radiogroup'
|
||||||
|
>
|
||||||
<SearchTypeItem
|
<SearchTypeItem
|
||||||
selected={searchType === 'messages'}
|
selected={searchType === 'messages'}
|
||||||
onClick={setMessagesSearchType}
|
onClick={setMessagesSearchType}
|
||||||
|
role='radio'
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='search_bar.usage.search_type_messages'
|
id='search_bar.usage.search_type_messages'
|
||||||
@@ -68,6 +71,7 @@ const SearchTypeSelector = ({searchType, setSearchType}: Props) => {
|
|||||||
<SearchTypeItem
|
<SearchTypeItem
|
||||||
selected={searchType === 'files'}
|
selected={searchType === 'files'}
|
||||||
onClick={setFilesSearchType}
|
onClick={setFilesSearchType}
|
||||||
|
role='radio'
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='search_bar.usage.search_type_files'
|
id='search_bar.usage.search_type_files'
|
||||||
@@ -81,6 +85,7 @@ const SearchTypeSelector = ({searchType, setSearchType}: Props) => {
|
|||||||
key={pluginId}
|
key={pluginId}
|
||||||
selected={searchType === pluginId}
|
selected={searchType === pluginId}
|
||||||
onClick={() => setSearchType(pluginId)}
|
onClick={() => setSearchType(pluginId)}
|
||||||
|
role='radio'
|
||||||
>
|
>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<Component/>
|
<Component/>
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ export type Props = {
|
|||||||
id?: string;
|
id?: string;
|
||||||
onInput?: (e?: React.FormEvent<HTMLInputElement>) => void;
|
onInput?: (e?: React.FormEvent<HTMLInputElement>) => void;
|
||||||
tabIndex?: number;
|
tabIndex?: number;
|
||||||
|
role?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A component that can be used to make controlled inputs that function properly in certain
|
// A component that can be used to make controlled inputs that function properly in certain
|
||||||
|
|||||||
@@ -466,16 +466,18 @@ const Search = ({
|
|||||||
const renderSearchBar = (): JSX.Element => (
|
const renderSearchBar = (): JSX.Element => (
|
||||||
<>
|
<>
|
||||||
<div className='sidebar-collapse__container'>
|
<div className='sidebar-collapse__container'>
|
||||||
<div
|
<button
|
||||||
id={isSideBarRight ? 'sbrSidebarCollapse' : 'sidebarCollapse'}
|
id={isSideBarRight ? 'sbrSidebarCollapse' : 'sidebarCollapse'}
|
||||||
className='sidebar-collapse'
|
className='sidebar-collapse'
|
||||||
onClick={handleClose}
|
onClick={handleClose}
|
||||||
|
aria-label={intl.formatMessage({id: 'channel_header.back', defaultMessage: 'Back to channel'})}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className='fa fa-2x fa-angle-left'
|
className='fa fa-2x fa-angle-left'
|
||||||
title={intl.formatMessage({id: 'generic_icons.back', defaultMessage: 'Back Icon'})}
|
title={intl.formatMessage({id: 'generic_icons.back', defaultMessage: 'Back Icon'})}
|
||||||
|
aria-hidden='true'
|
||||||
/>
|
/>
|
||||||
</div>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<SearchBar
|
<SearchBar
|
||||||
updateHighlightedSearchHint={updateHighlightedSearchHint}
|
updateHighlightedSearchHint={updateHighlightedSearchHint}
|
||||||
|
|||||||
@@ -3478,6 +3478,7 @@
|
|||||||
"channel_bookmarks.editBookmarkLabel": "Bookmark menu",
|
"channel_bookmarks.editBookmarkLabel": "Bookmark menu",
|
||||||
"channel_bookmarks.open": "Open",
|
"channel_bookmarks.open": "Open",
|
||||||
"channel_groups": "{channel} Groups",
|
"channel_groups": "{channel} Groups",
|
||||||
|
"channel_header.back": "Back to channel",
|
||||||
"channel_header.channel_settings": "Channel Settings",
|
"channel_header.channel_settings": "Channel Settings",
|
||||||
"channel_header.channelFiles": "Channel files",
|
"channel_header.channelFiles": "Channel files",
|
||||||
"channel_header.channelHasGuests": "Channel has guests",
|
"channel_header.channelHasGuests": "Channel has guests",
|
||||||
@@ -5191,6 +5192,7 @@
|
|||||||
"search_bar.files_tab": "Files",
|
"search_bar.files_tab": "Files",
|
||||||
"search_bar.messages_tab": "Messages",
|
"search_bar.messages_tab": "Messages",
|
||||||
"search_bar.search": "Search",
|
"search_bar.search": "Search",
|
||||||
|
"search_bar.search_box": "Search box",
|
||||||
"search_bar.search_files": "Search files",
|
"search_bar.search_files": "Search files",
|
||||||
"search_bar.search_messages": "Search messages",
|
"search_bar.search_messages": "Search messages",
|
||||||
"search_bar.search_types.files": "FILES",
|
"search_bar.search_types.files": "FILES",
|
||||||
@@ -5204,6 +5206,7 @@
|
|||||||
"search_bar.usage.title_files": "File search options",
|
"search_bar.usage.title_files": "File search options",
|
||||||
"search_bar.usage.title_messages": "Message search options",
|
"search_bar.usage.title_messages": "Message search options",
|
||||||
"search_bar.users": "Users",
|
"search_bar.users": "Users",
|
||||||
|
"search_box_suggestions.suggestions_readout": "{label} ({idx} of {total} results available)",
|
||||||
"search_file_extension_suggestion.aria_label": "{fileType} (.{extension})",
|
"search_file_extension_suggestion.aria_label": "{fileType} (.{extension})",
|
||||||
"search_files_list_option.after": "Files after a date",
|
"search_files_list_option.after": "Files after a date",
|
||||||
"search_files_list_option.before": "Files before a date",
|
"search_files_list_option.before": "Files before a date",
|
||||||
|
|||||||
@@ -277,6 +277,8 @@
|
|||||||
height: 48px;
|
height: 48px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
transform: translateX(0);
|
transform: translateX(0);
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user