mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
[MM-51680] : Revert New Browse Channels UI modal changes (https://github.com/mattermost/mattermost-webapp/pull/11262) (#22613)
* Revert https://github.com/mattermost/mattermost-webapp/pull/11262 * fix missing translation * fix indentation issue * fix imports in Update accessibility_modals_dialogs_spec.js --------- Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
@@ -97,7 +97,7 @@ describe('Leave an archived channel', () => {
|
||||
// # More channels modal opens
|
||||
cy.get('#moreChannelsModal').should('be.visible').within(() => {
|
||||
// # Click on dropdown
|
||||
cy.findByText('Channel Type: Public').should('be.visible').click();
|
||||
cy.findByText('Show: Public Channels').should('be.visible').click();
|
||||
|
||||
// # Click archived channels
|
||||
cy.findByText('Archived Channels').click();
|
||||
@@ -143,9 +143,9 @@ describe('Leave an archived channel', () => {
|
||||
cy.get('#showMoreChannels').click();
|
||||
|
||||
// # More channels modal opens
|
||||
cy.get('#moreChannelsModal').should('be.visible').within(() => {
|
||||
cy.get('.more-modal').should('be.visible').within(() => {
|
||||
// # Public channel list opens by default
|
||||
cy.findByText('Channel Type: Public').should('be.visible').click();
|
||||
cy.findByText('Show: Public Channels').should('be.visible').click();
|
||||
|
||||
// # Click on archived channels
|
||||
cy.findByText('Archived Channels').click();
|
||||
@@ -196,9 +196,9 @@ describe('Leave an archived channel', () => {
|
||||
cy.get('#showMoreChannels').click();
|
||||
|
||||
// # More channels modal opens
|
||||
cy.get('#moreChannelsModal').should('be.visible').within(() => {
|
||||
cy.get('.more-modal').should('be.visible').within(() => {
|
||||
// # Public channels are shown by default
|
||||
cy.findByText('Channel Type: Public').should('be.visible').click();
|
||||
cy.findByText('Show: Public Channels').should('be.visible').click();
|
||||
|
||||
// # Go to archived channels
|
||||
cy.findByText('Archived Channels').click();
|
||||
@@ -250,9 +250,9 @@ describe('Leave an archived channel', () => {
|
||||
cy.get('#showMoreChannels').click();
|
||||
|
||||
// # More channels modal opens
|
||||
cy.get('#moreChannelsModal').should('be.visible').within(() => {
|
||||
cy.get('.more-modal').should('be.visible').within(() => {
|
||||
// # Show public channels is visible by default
|
||||
cy.findByText('Channel Type: Public').should('be.visible').click();
|
||||
cy.findByText('Show: Public Channels').should('be.visible').click();
|
||||
|
||||
// # Go to archived channels
|
||||
cy.findByText('Archived Channels').click();
|
||||
@@ -286,7 +286,7 @@ describe('Leave an archived channel', () => {
|
||||
|
||||
// # More channels modal opens and lands on public channels
|
||||
cy.get('#moreChannelsModal').should('be.visible').within(() => {
|
||||
cy.findByText('Channel Type: Public').should('be.visible').click();
|
||||
cy.findByText('Show: Public Channels').should('be.visible').click();
|
||||
|
||||
// # Go to archived channels
|
||||
cy.findByText('Archived Channels').click();
|
||||
|
||||
@@ -65,7 +65,7 @@ describe('Channels', () => {
|
||||
|
||||
cy.get('#moreChannelsModal').should('be.visible').within(() => {
|
||||
// * Dropdown should be visible, defaulting to "Public Channels"
|
||||
cy.get('#channelsMoreDropdown').should('be.visible').and('contain', 'Channel Type: Public').wait(TIMEOUTS.HALF_SEC);
|
||||
cy.get('#channelsMoreDropdown').should('be.visible').and('contain', 'Show: Public Channels').wait(TIMEOUTS.HALF_SEC);
|
||||
|
||||
cy.get('#searchChannelsTextbox').should('be.visible').type(testChannel.display_name).wait(TIMEOUTS.HALF_SEC);
|
||||
cy.get('#moreChannelsList').should('be.visible').children().should('have.length', 1).within(() => {
|
||||
@@ -80,8 +80,8 @@ describe('Channels', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// # Verify that the modal is not closed
|
||||
cy.get('#moreChannelsModal').should('exist');
|
||||
// # Verify that the modal is closed and it's redirected to the selected channel
|
||||
cy.get('#moreChannelsModal').should('not.exist');
|
||||
cy.url().should('include', `/${testTeam.name}/channels/${testChannel.name}`);
|
||||
|
||||
// # Login as channel admin and go directly to the channel
|
||||
@@ -113,7 +113,7 @@ describe('Channels', () => {
|
||||
cy.findByText('Archived Channels').should('be.visible').click();
|
||||
|
||||
// * Channel test should be visible as an archived channel in the list
|
||||
cy.wrap(el).should('contain', 'Channel Type: Archived');
|
||||
cy.wrap(el).should('contain', 'Show: Archived Channels');
|
||||
});
|
||||
|
||||
cy.get('#searchChannelsTextbox').should('be.visible').type(testChannel.display_name).wait(TIMEOUTS.HALF_SEC);
|
||||
@@ -196,7 +196,7 @@ describe('Channels', () => {
|
||||
|
||||
// * Dropdown should be visible, defaulting to "Public Channels"
|
||||
cy.get('#channelsMoreDropdown').should('be.visible').within((el) => {
|
||||
cy.wrap(el).should('contain', 'Channel Type: Public');
|
||||
cy.wrap(el).should('contain', 'Show: Public Channels');
|
||||
});
|
||||
|
||||
// * Users should be able to type and search
|
||||
@@ -207,12 +207,12 @@ describe('Channels', () => {
|
||||
|
||||
cy.get('#moreChannelsModal').should('be.visible').within(() => {
|
||||
// * Users should be able to switch to "Archived Channels" list
|
||||
cy.get('#channelsMoreDropdown').should('be.visible').and('contain', 'Channel Type: Public').click().within((el) => {
|
||||
cy.get('#channelsMoreDropdown').should('be.visible').and('contain', 'Show: Public Channels').click().within((el) => {
|
||||
// # Click on archived channels item
|
||||
cy.findByText('Archived Channels').should('be.visible').click();
|
||||
|
||||
// * Modal should show the archived channels list
|
||||
cy.wrap(el).should('contain', 'Channel Type: Archived');
|
||||
cy.wrap(el).should('contain', 'Show: Archived Channels');
|
||||
}).wait(TIMEOUTS.HALF_SEC);
|
||||
cy.get('#searchChannelsTextbox').clear();
|
||||
cy.get('#moreChannelsList').should('be.visible').children().should('have.length', 2);
|
||||
@@ -250,7 +250,7 @@ function verifyMoreChannelsModal(isEnabled) {
|
||||
// * Verify that the more channels modal is open and with or without option to view archived channels
|
||||
cy.get('#moreChannelsModal').should('be.visible').within(() => {
|
||||
if (isEnabled) {
|
||||
cy.get('#channelsMoreDropdown').should('be.visible').and('have.text', 'Channel Type: Public');
|
||||
cy.get('#channelsMoreDropdown').should('be.visible').and('have.text', 'Show: Public Channels');
|
||||
} else {
|
||||
cy.get('#channelsMoreDropdown').should('not.exist');
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
// Group: @channels @channel
|
||||
|
||||
function verifyNoChannelToJoinMessage(isVisible) {
|
||||
cy.findByText('No public channels').should(isVisible ? 'be.visible' : 'not.exist');
|
||||
cy.findByText('No more channels to join').should(isVisible ? 'be.visible' : 'not.exist');
|
||||
cy.findByText('Click \'Create New Channel\' to make a new one').should(isVisible ? 'be.visible' : 'not.exist');
|
||||
}
|
||||
|
||||
describe('more public channels', () => {
|
||||
@@ -52,10 +53,7 @@ describe('more public channels', () => {
|
||||
cy.uiBrowseOrCreateChannel('Browse Channels').click();
|
||||
|
||||
// * Assert that the moreChannelsModel is visible
|
||||
cy.findByRole('dialog', {name: 'Browse Channels'}).should('be.visible').within(() => {
|
||||
// # Click hide joined checkbox
|
||||
cy.findByText('Hide Joined').should('be.visible').click();
|
||||
|
||||
cy.findByRole('dialog', {name: 'More Channels'}).should('be.visible').within(() => {
|
||||
// * Assert that the moreChannelsList is visible and the number of channels is 31
|
||||
cy.get('#moreChannelsList').should('be.visible').children().should('have.length', 31);
|
||||
|
||||
@@ -88,9 +86,9 @@ describe('more public channels', () => {
|
||||
cy.uiBrowseOrCreateChannel('Browse Channels').click();
|
||||
|
||||
// * Assert the moreChannelsModel is visible
|
||||
cy.findByRole('dialog', {name: 'Browse Channels'}).should('be.visible').within(() => {
|
||||
// # Click hide joined checkbox
|
||||
cy.findByText('Hide Joined').should('be.visible').click();
|
||||
cy.findByRole('dialog', {name: 'More Channels'}).should('be.visible').within(() => {
|
||||
// * Assert the moreChannelsList does have one child
|
||||
cy.get('#moreChannelsList').should('be.visible').children().should('have.length', 1);
|
||||
|
||||
// * Assert that the "No more channels to join" message is visible
|
||||
verifyNoChannelToJoinMessage(true);
|
||||
|
||||
@@ -68,13 +68,13 @@ describe('Channel sidebar', () => {
|
||||
cy.get('.AddChannelDropdown .MenuItem:contains(Browse Channels) button').should('be.visible').click();
|
||||
|
||||
// * Verify that the more channels modal is visible
|
||||
cy.get('#moreChannelsModal').should('be.visible');
|
||||
cy.get('.more-modal').should('be.visible');
|
||||
|
||||
// Click the Off-Topic channel
|
||||
cy.findByText('Off-Topic').should('be.visible').click();
|
||||
cy.get('.more-modal button:contains(Off-Topic)').should('be.visible').click();
|
||||
|
||||
// Verify that new channel is in the sidebar and is active
|
||||
cy.get('#moreChannelsModal').should('exist');
|
||||
cy.get('.more-modal').should('not.exist');
|
||||
cy.url().should('include', `/${teamName}/channels/off-topic`);
|
||||
cy.get('#channelHeaderTitle').should('contain', 'Off-Topic');
|
||||
cy.get('.SidebarChannel.active:contains(Off-Topic)').should('be.visible');
|
||||
|
||||
@@ -104,26 +104,30 @@ describe('Verify Accessibility Support in Modals & Dialogs', () => {
|
||||
cy.uiBrowseOrCreateChannel('Browse Channels').click();
|
||||
|
||||
// * Verify the accessibility support in More Channels Dialog
|
||||
cy.findByRole('dialog', {name: 'Browse Channels'}).within(() => {
|
||||
cy.findByRole('heading', {name: 'Browse Channels'});
|
||||
cy.findByRole('dialog', {name: 'More Channels'}).within(() => {
|
||||
cy.findByRole('heading', {name: 'More Channels'});
|
||||
|
||||
// * Verify the accessibility support in search input
|
||||
cy.findByPlaceholderText('Search channels');
|
||||
|
||||
cy.get('#moreChannelsList').should('be.visible').then((el) => {
|
||||
cy.waitUntil(() => cy.get('#moreChannelsList').then((el) => {
|
||||
return el[0].children.length === 2;
|
||||
});
|
||||
}));
|
||||
|
||||
// # Hide already joined channels
|
||||
cy.findByText('Hide Joined').click();
|
||||
|
||||
// # Focus on the Create Channel button and TAB three time
|
||||
cy.get('#createNewChannelButton').focus().tab().tab().tab();
|
||||
// # Focus on the Create Channel button and TAB twice
|
||||
cy.get('#createNewChannel').focus().tab().tab();
|
||||
|
||||
// * Verify channel name is highlighted and reader reads the channel name and channel description
|
||||
cy.get('#moreChannelsList').within(() => {
|
||||
cy.get('#moreChannelsList').children().eq(0).within(() => {
|
||||
const selectedChannel = getChannelAriaLabel(channel);
|
||||
cy.findByLabelText(selectedChannel).should('be.visible').should('be.focused');
|
||||
cy.findByLabelText(selectedChannel).should('be.focused');
|
||||
|
||||
// * Press Tab and verify if focus changes to Join button
|
||||
cy.focused().tab();
|
||||
cy.findByText('Join').parent().should('be.focused');
|
||||
|
||||
// * Verify previous button should no longer be focused
|
||||
cy.findByLabelText(selectedChannel).should('not.be.focused');
|
||||
});
|
||||
|
||||
// * Press Tab again and verify if focus changes to next row
|
||||
|
||||
@@ -234,7 +234,7 @@ context('ldap', () => {
|
||||
|
||||
// * Search private channel name and make sure it isn't there in public channel directory
|
||||
cy.get('#searchChannelsTextbox').type(testChannel.display_name);
|
||||
cy.get('#moreChannelsList').should('include.text', 'No results for');
|
||||
cy.get('#moreChannelsList').should('include.text', 'No more channels to join');
|
||||
});
|
||||
|
||||
it('MM-T2629 - Private to public - More....', () => {
|
||||
@@ -473,7 +473,7 @@ context('ldap', () => {
|
||||
|
||||
// * Search private channel name and make sure it isn't there in public channel directory
|
||||
cy.get('#searchChannelsTextbox').type(publicChannel.display_name);
|
||||
cy.get('#moreChannelsList').should('include.text', 'No results for');
|
||||
cy.get('#moreChannelsList').should('include.text', 'No more channels to join');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ describe('Actions.Channel', () => {
|
||||
}],
|
||||
}];
|
||||
|
||||
await testStore.dispatch(searchMoreChannels('', false, true));
|
||||
await testStore.dispatch(searchMoreChannels('', false));
|
||||
expect(testStore.getActions()).toEqual(expectedActions);
|
||||
});
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ export function loadChannelsForCurrentUser(): ActionFunc {
|
||||
};
|
||||
}
|
||||
|
||||
export function searchMoreChannels(term: string, showArchivedChannels: boolean, hideJoinedChannels: boolean): ActionFunc<Channel[], ServerError> {
|
||||
export function searchMoreChannels(term: string, showArchivedChannels: boolean): ActionFunc<Channel[], ServerError> {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const teamId = getCurrentTeamId(state);
|
||||
@@ -121,7 +121,9 @@ export function searchMoreChannels(term: string, showArchivedChannels: boolean,
|
||||
const {data, error} = await dispatch(ChannelActions.searchChannels(teamId, term, showArchivedChannels));
|
||||
if (data) {
|
||||
const myMembers = getMyChannelMemberships(state);
|
||||
const channels = hideJoinedChannels ? (data as Channel[]).filter((channel) => !myMembers[channel.id]) : data;
|
||||
|
||||
// When searching public channels, only get channels user is not a member of
|
||||
const channels = showArchivedChannels ? data : (data as Channel[]).filter((c) => !myMembers[c.id]);
|
||||
return {data: channels};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`components/SearchableChannelList should match init snapshot 1`] = `
|
||||
<div
|
||||
className="filtered-user-list"
|
||||
>
|
||||
<div
|
||||
className="filter-row filter-row--full"
|
||||
>
|
||||
<div
|
||||
className="col-sm-12"
|
||||
>
|
||||
<QuickInput
|
||||
className="form-control filter-textbox"
|
||||
id="searchChannelsTextbox"
|
||||
inputComponent={
|
||||
Object {
|
||||
"$$typeof": Symbol(react.forward_ref),
|
||||
"render": [Function],
|
||||
}
|
||||
}
|
||||
onInput={[Function]}
|
||||
placeholder={
|
||||
Object {
|
||||
"defaultMessage": "Search channels",
|
||||
"id": "filtered_channels_list.search",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="more-modal__list"
|
||||
role="application"
|
||||
>
|
||||
<div
|
||||
id="moreChannelsList"
|
||||
>
|
||||
<LoadingScreen />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="filter-controls"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
@@ -1,39 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {SVGProps} from 'react';
|
||||
|
||||
const SvgComponent = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
width={140}
|
||||
height={141}
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
opacity={0.4}
|
||||
d='M37.593 38.008c4.754-4.815 10.754-7.295 17.989-7.428 7.101.133 13.065 2.601 17.892 7.428 4.815 4.827 7.295 10.791 7.428 17.892-.133 7.235-2.601 13.223-7.428 17.99-4.827 4.754-10.791 7.27-17.892 7.512-7.235-.254-13.223-2.758-17.99-7.513-4.754-4.766-7.258-10.766-7.512-18 .254-7.102 2.758-13.066 7.513-17.881Z'
|
||||
fill='#fff'
|
||||
/>
|
||||
<path
|
||||
d='M78.887 51.382c-2.151-6.992-6.225-12.225-12.226-15.69-6.001-3.465-12.57-4.376-19.701-2.743-3.9.995-7.297 2.717-10.22 5.162 3.269-3.567 7.415-6.037 12.428-7.416 7.13-1.633 13.732-.703 19.787 2.793s10.161 8.748 12.313 15.74c1.323 5.037 1.257 9.862-.21 14.47-1.454 4.614-4.066 8.49-7.84 11.611 2.833-3.087 4.783-6.713 5.844-10.894 1.05-4.187.991-8.522-.175-13.033Z'
|
||||
fill='#000'
|
||||
fillOpacity={0.4}
|
||||
/>
|
||||
<path
|
||||
d='M86.76 53.929c-.508-7.506-3.553-14.097-9.125-19.774-6.346-6.05-13.67-9.08-21.974-9.08-8.303 0-15.616 3.03-21.961 9.08-6.08 6.315-9.126 13.591-9.126 21.855 0 8.262 3.046 15.551 9.126 21.854 5.826 5.556 12.485 8.551 19.967 8.984 7.481.445 14.383-1.611 20.728-6.146l4.75 4.727 6.08-6.05-4.75-4.727c4.69-6.302 6.78-13.218 6.285-20.723Zm-13.126 19.87c-4.823 4.726-10.781 7.228-17.876 7.468-7.228-.252-13.21-2.742-17.973-7.469-4.75-4.727-7.252-10.692-7.506-17.885.254-7.06 2.756-12.99 7.506-17.789 4.75-4.787 10.745-7.252 17.973-7.385 7.095.133 13.053 2.586 17.876 7.385 4.81 4.8 7.288 10.73 7.421 17.79-.133 7.192-2.599 13.157-7.421 17.884Z'
|
||||
fill='#BABEC9'
|
||||
/>
|
||||
<path
|
||||
d='M106.202 114.187c-1.567.449-2.728.291-3.482-.472L78.06 86.651c-.753-.762-1.064-1.743-.945-2.954.12-1.211.874-2.567 2.262-4.093 1.507-1.393 2.847-2.192 4.044-2.385 1.196-.194 2.165.157 2.92 1.053l26.921 24.957c.753.763.873 1.901.37 3.427-.502 1.525-1.447 3.051-2.823 4.577-1.496 1.526-3.039 2.506-4.607 2.954Z'
|
||||
fill='#FFBC1F'
|
||||
/>
|
||||
<path
|
||||
d='m108.007 98.343-10.08 10.164-12.154-13.34 8.914-9.106 13.32 12.282Z'
|
||||
fill='#7A5600'
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default SvgComponent;
|
||||
@@ -133,7 +133,6 @@
|
||||
}
|
||||
|
||||
.GenericModal__header {
|
||||
width: 85%;
|
||||
padding: 0;
|
||||
border-top-left-radius: 12px;
|
||||
border-top-right-radius: 12px;
|
||||
|
||||
@@ -1,15 +1,53 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`components/MoreChannels should match snapshot and state 1`] = `
|
||||
<GenericModal
|
||||
<Modal
|
||||
animation={true}
|
||||
aria-labelledby="moreChannelsModalLabel"
|
||||
aria-modal={true}
|
||||
autoCloseOnCancelButton={true}
|
||||
autoCloseOnConfirmButton={false}
|
||||
compassDesign={true}
|
||||
enforceFocus={false}
|
||||
headerButton={
|
||||
<Memo(Connect(TeamPermissionGate))
|
||||
autoFocus={true}
|
||||
backdrop={true}
|
||||
bsClass="modal"
|
||||
dialogClassName="a11y__modal more-modal more-modal--action"
|
||||
dialogComponentClass={[Function]}
|
||||
enforceFocus={true}
|
||||
id="moreChannelsModal"
|
||||
keyboard={true}
|
||||
manager={
|
||||
ModalManager {
|
||||
"add": [Function],
|
||||
"containers": Array [],
|
||||
"data": Array [],
|
||||
"handleContainerOverflow": true,
|
||||
"hideSiblingNodes": true,
|
||||
"isTopModal": [Function],
|
||||
"modals": Array [],
|
||||
"remove": [Function],
|
||||
}
|
||||
}
|
||||
onExited={[Function]}
|
||||
onHide={[Function]}
|
||||
renderBackdrop={[Function]}
|
||||
restoreFocus={true}
|
||||
role="dialog"
|
||||
show={true}
|
||||
>
|
||||
<ModalHeader
|
||||
bsClass="modal-header"
|
||||
closeButton={true}
|
||||
closeLabel="Close"
|
||||
id="moreChannelsModalHeader"
|
||||
>
|
||||
<ModalTitle
|
||||
bsClass="modal-title"
|
||||
componentClass="h1"
|
||||
id="moreChannelsModalLabel"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="More Channels"
|
||||
id="more_channels.title"
|
||||
/>
|
||||
</ModalTitle>
|
||||
<Connect(TeamPermissionGate)
|
||||
permissions={
|
||||
Array [
|
||||
"create_public_channel",
|
||||
@@ -18,30 +56,74 @@ exports[`components/MoreChannels should match snapshot and state 1`] = `
|
||||
teamId="team_id"
|
||||
>
|
||||
<button
|
||||
aria-label="Create New Channel"
|
||||
className="btn outlineButton"
|
||||
id="createNewChannelButton"
|
||||
className="btn btn-primary channel-create-btn"
|
||||
id="createNewChannel"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Create New Channel"
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Create Channel"
|
||||
id="more_channels.create"
|
||||
/>
|
||||
</button>
|
||||
</Memo(Connect(TeamPermissionGate))>
|
||||
}
|
||||
id="moreChannelsModal"
|
||||
keyboardEscape={true}
|
||||
modalHeaderText={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Browse Channels"
|
||||
id="more_channels.title"
|
||||
</Connect(TeamPermissionGate)>
|
||||
</ModalHeader>
|
||||
<ModalBody
|
||||
bsClass="modal-body"
|
||||
componentClass="div"
|
||||
>
|
||||
<SearchableChannelList
|
||||
canShowArchivedChannels={true}
|
||||
channels={
|
||||
Array [
|
||||
Object {
|
||||
"create_at": 0,
|
||||
"creator_id": "id",
|
||||
"delete_at": 0,
|
||||
"display_name": "name",
|
||||
"group_constrained": false,
|
||||
"header": "header",
|
||||
"id": "channel_id",
|
||||
"last_post_at": 0,
|
||||
"last_root_post_at": 0,
|
||||
"name": "DN",
|
||||
"purpose": "purpose",
|
||||
"scheme_id": "id",
|
||||
"team_id": "team_id",
|
||||
"type": "O",
|
||||
"update_at": 0,
|
||||
},
|
||||
]
|
||||
}
|
||||
channelsPerPage={50}
|
||||
handleJoin={[Function]}
|
||||
isSearch={false}
|
||||
loading={false}
|
||||
nextPage={[Function]}
|
||||
noResultsText={
|
||||
<Memo(Connect(TeamPermissionGate))
|
||||
permissions={
|
||||
Array [
|
||||
"create_public_channel",
|
||||
"create_private_channel",
|
||||
]
|
||||
}
|
||||
teamId="team_id"
|
||||
>
|
||||
<p
|
||||
className="secondary-message"
|
||||
>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Click 'Create New Channel' to make a new one"
|
||||
id="more_channels.createClick"
|
||||
/>
|
||||
</p>
|
||||
</Memo(Connect(TeamPermissionGate))>
|
||||
}
|
||||
search={[Function]}
|
||||
shouldShowArchivedChannels={false}
|
||||
toggleArchivedChannels={[Function]}
|
||||
/>
|
||||
}
|
||||
onExited={[Function]}
|
||||
show={true}
|
||||
>
|
||||
<LoadingScreen />
|
||||
</GenericModal>
|
||||
</ModalBody>
|
||||
</Modal>
|
||||
`;
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`components/SearchableChannelList should match init snapshot 1`] = `
|
||||
<div
|
||||
className="filtered-user-list"
|
||||
>
|
||||
<div
|
||||
className="filter-row filter-row--full"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
id="searchIcon"
|
||||
>
|
||||
<MagnifyIcon
|
||||
size={18}
|
||||
/>
|
||||
</span>
|
||||
<QuickInput
|
||||
aria-label="Search Channels"
|
||||
className="form-control filter-textbox"
|
||||
clearable={true}
|
||||
id="searchChannelsTextbox"
|
||||
inputComponent={
|
||||
Object {
|
||||
"$$typeof": Symbol(react.forward_ref),
|
||||
"render": [Function],
|
||||
}
|
||||
}
|
||||
onClear={[Function]}
|
||||
onInput={[Function]}
|
||||
placeholder={
|
||||
Object {
|
||||
"defaultMessage": "Search channels",
|
||||
"id": "filtered_channels_list.search",
|
||||
}
|
||||
}
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="more-modal__dropdown"
|
||||
>
|
||||
<span
|
||||
id="channelCountLabel"
|
||||
>
|
||||
0 Results
|
||||
</span>
|
||||
<div
|
||||
id="modalPreferenceContainer"
|
||||
>
|
||||
<div
|
||||
id="hideJoinedPreferenceCheckbox"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<button
|
||||
aria-label="Hide joined channels checkbox, not checked"
|
||||
className="get-app__checkbox"
|
||||
/>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Hide Joined"
|
||||
id="more_channels.hide_joined"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="more-modal__list"
|
||||
role="application"
|
||||
tabIndex={-1}
|
||||
>
|
||||
<div
|
||||
id="moreChannelsList"
|
||||
tabIndex={-1}
|
||||
>
|
||||
<LoadingScreen />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="filter-controls"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
@@ -12,29 +12,24 @@ import {getConfig} from 'mattermost-redux/selectors/entities/general';
|
||||
import {Action, ActionResult} from 'mattermost-redux/types/actions';
|
||||
import {getCurrentTeam} from 'mattermost-redux/selectors/entities/teams';
|
||||
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
|
||||
import {getChannels, getArchivedChannels, joinChannel, getChannelStats} from 'mattermost-redux/actions/channels';
|
||||
import {getChannelsInCurrentTeam, getMyChannelMemberships, getAllChannelStats} from 'mattermost-redux/selectors/entities/channels';
|
||||
|
||||
import {Constants, StoragePrefixes} from 'utils/constants';
|
||||
import {getChannels, getArchivedChannels, joinChannel} from 'mattermost-redux/actions/channels';
|
||||
import {getOtherChannels, getChannelsInCurrentTeam} from 'mattermost-redux/selectors/entities/channels';
|
||||
|
||||
import {searchMoreChannels} from 'actions/channel_actions';
|
||||
import {openModal, closeModal} from 'actions/views/modals';
|
||||
import {setGlobalItem} from 'actions/storage';
|
||||
import {closeRightHandSide} from 'actions/views/rhs';
|
||||
|
||||
import {getIsRhsOpen, getRhsState} from 'selectors/rhs';
|
||||
|
||||
import {GlobalState} from 'types/store';
|
||||
import {ModalData} from 'types/actions';
|
||||
|
||||
import {makeGetGlobalItem} from 'selectors/storage';
|
||||
import {GlobalState} from 'types/store';
|
||||
|
||||
import MoreChannels from './more_channels';
|
||||
|
||||
const getChannelsWithoutArchived = createSelector(
|
||||
'getChannelsWithoutArchived',
|
||||
getChannelsInCurrentTeam,
|
||||
(channels: Channel[]) => channels && channels.filter((c) => c.delete_at === 0 && c.type !== Constants.PRIVATE_CHANNEL),
|
||||
const getNotArchivedOtherChannels = createSelector(
|
||||
'getNotArchivedOtherChannels',
|
||||
getOtherChannels,
|
||||
(channels: Channel[]) => channels && channels.filter((c) => c.delete_at === 0),
|
||||
);
|
||||
|
||||
const getArchivedOtherChannels = createSelector(
|
||||
@@ -45,19 +40,15 @@ const getArchivedOtherChannels = createSelector(
|
||||
|
||||
function mapStateToProps(state: GlobalState) {
|
||||
const team = getCurrentTeam(state) || {};
|
||||
const getGlobalItem = makeGetGlobalItem(StoragePrefixes.HIDE_JOINED_CHANNELS, 'false');
|
||||
|
||||
return {
|
||||
channels: getChannelsWithoutArchived(state) || [],
|
||||
channels: getNotArchivedOtherChannels(state) || [],
|
||||
archivedChannels: getArchivedOtherChannels(state) || [],
|
||||
currentUserId: getCurrentUserId(state),
|
||||
teamId: team.id,
|
||||
teamName: team.name,
|
||||
channelsRequestStarted: state.requests.channels.getChannels.status === RequestStatus.STARTED,
|
||||
canShowArchivedChannels: (getConfig(state).ExperimentalViewArchivedChannels === 'true'),
|
||||
myChannelMemberships: getMyChannelMemberships(state) || {},
|
||||
allChannelStats: getAllChannelStats(state) || {},
|
||||
shouldHideJoinedChannels: getGlobalItem(state) === 'true',
|
||||
rhsState: getRhsState(state),
|
||||
rhsOpen: getIsRhsOpen(state),
|
||||
};
|
||||
@@ -70,8 +61,6 @@ type Actions = {
|
||||
searchMoreChannels: (term: string, shouldShowArchivedChannels: boolean) => Promise<ActionResult>;
|
||||
openModal: <P>(modalData: ModalData<P>) => void;
|
||||
closeModal: (modalId: string) => void;
|
||||
getChannelStats: (channelId: string) => void;
|
||||
setGlobalItem: (name: string, value: string) => void;
|
||||
closeRightHandSide: () => void;
|
||||
}
|
||||
|
||||
@@ -84,8 +73,6 @@ function mapDispatchToProps(dispatch: Dispatch) {
|
||||
searchMoreChannels,
|
||||
openModal,
|
||||
closeModal,
|
||||
getChannelStats,
|
||||
setGlobalItem,
|
||||
closeRightHandSide,
|
||||
}, dispatch),
|
||||
};
|
||||
|
||||
@@ -1,295 +0,0 @@
|
||||
@charset 'UTF-8';
|
||||
|
||||
#moreChannelsModal {
|
||||
.modal-content {
|
||||
min-height: 600px;
|
||||
max-height: calc(50vh - 240px);
|
||||
}
|
||||
|
||||
.modal-dialog {
|
||||
margin-top: calc(45vh - 240px) !important;
|
||||
}
|
||||
|
||||
.filter-row--full {
|
||||
position: relative;
|
||||
margin: 0 32px;
|
||||
|
||||
.input-clear {
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
}
|
||||
|
||||
#searchIcon {
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
left: 16px;
|
||||
color: rgba(var(--center-channel-color-rgb), 0.64);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#searchChannelsTextbox {
|
||||
height: 48px;
|
||||
padding-left: 40px;
|
||||
border: 1px solid rgba(var(--center-channel-color-rgb), 0.16);
|
||||
box-shadow: none;
|
||||
font-size: 16px;
|
||||
|
||||
&::placeholder {
|
||||
color: var(--center-channel-color);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border: 2px solid var(--button-bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.more-modal__dropdown {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 32px;
|
||||
border-bottom: solid 1px rgba(var(--center-channel-color-rgb), 0.16);
|
||||
margin: 0;
|
||||
|
||||
span {
|
||||
color: rgba(var(--center-channel-color-rgb), 0.64);
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.MenuItem__primary-text {
|
||||
width: 100%;
|
||||
color: var(--center-channel-color);
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
|
||||
svg {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.Menu__content {
|
||||
border-color: rgba(var(--center-channel-color-rgb), 0.16);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
#channelCountLabel {
|
||||
color: var(--center-channel-color);
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
#modalPreferenceContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.get-app__checkbox {
|
||||
display: flex;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
align-items: center;
|
||||
border: 1px solid rgba(var(--center-channel-color-rgb), 0.24);
|
||||
}
|
||||
|
||||
#hideJoinedPreferenceCheckbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#channelsMoreDropdown {
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
#menuWrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 4px 6px 4px 8px;
|
||||
}
|
||||
|
||||
.MenuWrapper:hover {
|
||||
background-color: rgba(var(--center-channel-color-rgb), 0.08);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.MenuWrapper--open {
|
||||
background-color: rgba(var(--button-bg-rgb), 0.12);
|
||||
border-radius: 4px;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(var(--button-bg-rgb), 0.12);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 15px 0 0;
|
||||
|
||||
.filtered-user-list {
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
.more-modal__row {
|
||||
padding: 0 32px;
|
||||
border-bottom: none;
|
||||
|
||||
.more-modal__details {
|
||||
padding-left: 0;
|
||||
color: rgba(var(--center-channel-color-rgb), 0.56);
|
||||
|
||||
svg {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.more-modal__name {
|
||||
align-items: center;
|
||||
margin-top: 0;
|
||||
|
||||
span {
|
||||
color: var(--center-channel-color);
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
#channelPurposeContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
|
||||
.dot {
|
||||
width: 3px;
|
||||
height: 3px;
|
||||
flex-shrink: 0;
|
||||
background-color: rgba(var(--center-channel-color-rgb), 0.56);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.more-modal__description {
|
||||
margin-left: 4px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
#membershipIndicatorContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
span,
|
||||
svg {
|
||||
color: var(--online-indicator);
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
margin: 0 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
line-height: 12px;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.more-modal__actions {
|
||||
button {
|
||||
display: none;
|
||||
min-width: 54px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.more-modal__row:hover,
|
||||
.more-modal__row:focus {
|
||||
background-color: rgba(var(--center-channel-color-rgb), 0.08);
|
||||
cursor: pointer;
|
||||
|
||||
.more-modal__actions {
|
||||
.primaryButton,
|
||||
.outlineButton {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-group {
|
||||
padding: 0 32px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
.GenericModal__header {
|
||||
display: flex;
|
||||
width: 95%;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.close {
|
||||
top: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.outlineButton {
|
||||
border: 1px solid var(--button-bg);
|
||||
background: none;
|
||||
border-radius: 4px;
|
||||
color: var(--button-bg);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.outlineButton:hover {
|
||||
background-color: rgba(var(--button-bg-rgb), 0.08);
|
||||
}
|
||||
|
||||
.filter-controls {
|
||||
padding: 0;
|
||||
|
||||
button {
|
||||
min-width: 72px;
|
||||
margin: 8px 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#moreChannelsList {
|
||||
.primary-message {
|
||||
margin-top: 8px;
|
||||
color: var(--center-channel-color);
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
.secondary-message {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.primaryButton {
|
||||
background-color: var(--button-bg);
|
||||
border-radius: 4px;
|
||||
color: var(--button-color);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#createNewChannelButton {
|
||||
padding: 10px 20px;
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import {shallow} from 'enzyme';
|
||||
import {ActionResult} from 'mattermost-redux/types/actions';
|
||||
|
||||
import MoreChannels, {Props} from 'components/more_channels/more_channels';
|
||||
import SearchableChannelList from 'components/more_channels/searchable_channel_list.jsx';
|
||||
import SearchableChannelList from 'components/searchable_channel_list.jsx';
|
||||
|
||||
import {getHistory} from 'utils/browser_history';
|
||||
import {TestHelper} from 'utils/test_helper';
|
||||
@@ -59,16 +59,7 @@ describe('components/MoreChannels', () => {
|
||||
};
|
||||
|
||||
const baseProps: Props = {
|
||||
channels: [
|
||||
TestHelper.getChannelMock({
|
||||
id: 'channel-1',
|
||||
name: 'channel-1',
|
||||
}),
|
||||
TestHelper.getChannelMock({
|
||||
id: 'channel-2',
|
||||
name: 'channel-2',
|
||||
}),
|
||||
],
|
||||
channels: [TestHelper.getChannelMock({})],
|
||||
archivedChannels: [TestHelper.getChannelMock({
|
||||
id: 'channel_id_2',
|
||||
team_id: 'channel_team_2',
|
||||
@@ -82,14 +73,6 @@ describe('components/MoreChannels', () => {
|
||||
teamName: 'team_name',
|
||||
channelsRequestStarted: false,
|
||||
canShowArchivedChannels: true,
|
||||
myChannelMemberships: {
|
||||
'channel-2': TestHelper.getChannelMembershipMock({
|
||||
channel_id: 'channel-2',
|
||||
user_id: 'user-1',
|
||||
}),
|
||||
},
|
||||
allChannelStats: {},
|
||||
shouldHideJoinedChannels: false,
|
||||
actions: {
|
||||
getChannels: jest.fn(),
|
||||
getArchivedChannels: jest.fn(),
|
||||
@@ -97,8 +80,6 @@ describe('components/MoreChannels', () => {
|
||||
searchMoreChannels: jest.fn(channelActions.searchMoreChannels),
|
||||
openModal: jest.fn(),
|
||||
closeModal: jest.fn(),
|
||||
getChannelStats: jest.fn(),
|
||||
setGlobalItem: jest.fn(),
|
||||
closeRightHandSide: jest.fn(),
|
||||
},
|
||||
};
|
||||
@@ -110,6 +91,7 @@ describe('components/MoreChannels', () => {
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(wrapper.state('searchedChannels')).toEqual([]);
|
||||
expect(wrapper.state('show')).toEqual(true);
|
||||
expect(wrapper.state('shouldShowArchivedChannels')).toEqual(false);
|
||||
expect(wrapper.state('search')).toEqual(false);
|
||||
expect(wrapper.state('serverError')).toBeNull();
|
||||
@@ -120,6 +102,16 @@ describe('components/MoreChannels', () => {
|
||||
expect(wrapper.instance().props.actions.getChannels).toHaveBeenCalledWith(wrapper.instance().props.teamId, 0, 100);
|
||||
});
|
||||
|
||||
test('should match state on handleHide', () => {
|
||||
const wrapper = shallow<MoreChannels>(
|
||||
<MoreChannels {...baseProps}/>,
|
||||
);
|
||||
wrapper.setState({show: true});
|
||||
|
||||
wrapper.instance().handleHide();
|
||||
expect(wrapper.state('show')).toEqual(false);
|
||||
});
|
||||
|
||||
test('should call closeModal on handleExit', () => {
|
||||
const wrapper = shallow<MoreChannels>(
|
||||
<MoreChannels {...baseProps}/>,
|
||||
@@ -160,7 +152,7 @@ describe('components/MoreChannels', () => {
|
||||
<MoreChannels {...baseProps}/>,
|
||||
);
|
||||
|
||||
wrapper.setState({loading: false, search: true, searching: true});
|
||||
wrapper.setState({search: true, searching: true});
|
||||
const searchList = wrapper.find(SearchableChannelList);
|
||||
expect(searchList.props().loading).toEqual(true);
|
||||
});
|
||||
@@ -219,6 +211,7 @@ describe('components/MoreChannels', () => {
|
||||
process.nextTick(() => {
|
||||
expect(getHistory().push).toHaveBeenCalledTimes(1);
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
expect(wrapper.state('show')).toEqual(false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -256,7 +249,7 @@ describe('components/MoreChannels', () => {
|
||||
|
||||
jest.runOnlyPendingTimers();
|
||||
expect(wrapper.instance().props.actions.searchMoreChannels).toHaveBeenCalledTimes(1);
|
||||
expect(wrapper.instance().props.actions.searchMoreChannels).toHaveBeenCalledWith('fail', false, false);
|
||||
expect(wrapper.instance().props.actions.searchMoreChannels).toHaveBeenCalledWith('fail', false);
|
||||
process.nextTick(() => {
|
||||
expect(wrapper.state('search')).toEqual(true);
|
||||
expect(wrapper.state('searching')).toEqual(false);
|
||||
@@ -283,7 +276,7 @@ describe('components/MoreChannels', () => {
|
||||
|
||||
jest.runOnlyPendingTimers();
|
||||
expect(wrapper.instance().props.actions.searchMoreChannels).toHaveBeenCalledTimes(1);
|
||||
expect(wrapper.instance().props.actions.searchMoreChannels).toHaveBeenCalledWith('channel', false, false);
|
||||
expect(wrapper.instance().props.actions.searchMoreChannels).toHaveBeenCalledWith('channel', false);
|
||||
process.nextTick(() => {
|
||||
expect(wrapper.state('search')).toEqual(true);
|
||||
expect(wrapper.state('searching')).toEqual(false);
|
||||
@@ -310,7 +303,7 @@ describe('components/MoreChannels', () => {
|
||||
|
||||
jest.runOnlyPendingTimers();
|
||||
expect(wrapper.instance().props.actions.searchMoreChannels).toHaveBeenCalledTimes(1);
|
||||
expect(wrapper.instance().props.actions.searchMoreChannels).toHaveBeenCalledWith('channel', true, false);
|
||||
expect(wrapper.instance().props.actions.searchMoreChannels).toHaveBeenCalledWith('channel', true);
|
||||
process.nextTick(() => {
|
||||
expect(wrapper.state('search')).toEqual(true);
|
||||
expect(wrapper.state('searching')).toEqual(false);
|
||||
@@ -318,16 +311,4 @@ describe('components/MoreChannels', () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('should hide joined channels from channels props when shouldHideJoinedChannels prop is true', () => {
|
||||
const props = {
|
||||
...baseProps,
|
||||
shouldHideJoinedChannels: true,
|
||||
};
|
||||
const wrapper = shallow<MoreChannels>(
|
||||
<MoreChannels {...props}/>,
|
||||
);
|
||||
|
||||
expect(wrapper.instance().activeChannels).not.toContain(baseProps.channels[1]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,32 +2,23 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {Modal} from 'react-bootstrap';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import {ActionResult} from 'mattermost-redux/types/actions';
|
||||
import {Channel, ChannelMembership, ChannelStats} from '@mattermost/types/channels';
|
||||
import {Channel} from '@mattermost/types/channels';
|
||||
import Permissions from 'mattermost-redux/constants/permissions';
|
||||
|
||||
import {RelationOneToOne} from '@mattermost/types/utilities';
|
||||
|
||||
import NewChannelModal from 'components/new_channel_modal/new_channel_modal';
|
||||
import SearchableChannelList from 'components/searchable_channel_list.jsx';
|
||||
import TeamPermissionGate from 'components/permissions_gates/team_permission_gate';
|
||||
import GenericModal from 'components/generic_modal';
|
||||
import LoadingScreen from 'components/loading_screen';
|
||||
|
||||
import {ModalData} from 'types/actions';
|
||||
import {RhsState} from 'types/store/rhs';
|
||||
|
||||
import {getHistory} from 'utils/browser_history';
|
||||
import {ModalIdentifiers, StoragePrefixes, RHSStates} from 'utils/constants';
|
||||
import {ModalIdentifiers, RHSStates} from 'utils/constants';
|
||||
import {getRelativeChannelURL} from 'utils/url';
|
||||
import {localizeMessage} from 'utils/utils';
|
||||
|
||||
import SearchableChannelList from './searchable_channel_list';
|
||||
|
||||
import './more_channels.scss';
|
||||
|
||||
const CHANNELS_CHUNK_SIZE = 50;
|
||||
const CHANNELS_PER_PAGE = 50;
|
||||
@@ -37,15 +28,9 @@ type Actions = {
|
||||
getChannels: (teamId: string, page: number, perPage: number) => void;
|
||||
getArchivedChannels: (teamId: string, page: number, channelsPerPage: number) => void;
|
||||
joinChannel: (currentUserId: string, teamId: string, channelId: string) => Promise<ActionResult>;
|
||||
searchMoreChannels: (term: string, shouldShowArchivedChannels: boolean, shouldHideJoinedChannels: boolean) => Promise<ActionResult>;
|
||||
searchMoreChannels: (term: string, shouldShowArchivedChannels: boolean) => Promise<ActionResult>;
|
||||
openModal: <P>(modalData: ModalData<P>) => void;
|
||||
closeModal: (modalId: string) => void;
|
||||
getChannelStats: (channelId: string) => void;
|
||||
|
||||
/*
|
||||
* Function to set a key-value pair in the local storage
|
||||
*/
|
||||
setGlobalItem: (name: string, value: string) => void;
|
||||
closeRightHandSide: () => void;
|
||||
}
|
||||
|
||||
@@ -58,27 +43,23 @@ export type Props = {
|
||||
channelsRequestStarted?: boolean;
|
||||
canShowArchivedChannels?: boolean;
|
||||
morePublicChannelsModalType?: string;
|
||||
myChannelMemberships: RelationOneToOne<Channel, ChannelMembership>;
|
||||
allChannelStats: RelationOneToOne<Channel, ChannelStats>;
|
||||
shouldHideJoinedChannels: boolean;
|
||||
rhsState?: RhsState;
|
||||
rhsOpen?: boolean;
|
||||
actions: Actions;
|
||||
}
|
||||
|
||||
type State = {
|
||||
show: boolean;
|
||||
shouldShowArchivedChannels: boolean;
|
||||
search: boolean;
|
||||
searchedChannels: Channel[];
|
||||
serverError: React.ReactNode | string;
|
||||
searching: boolean;
|
||||
searchTerm: string;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
export default class MoreChannels extends React.PureComponent<Props, State> {
|
||||
public searchTimeoutId: number;
|
||||
activeChannels: Channel[] = [];
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
@@ -86,27 +67,25 @@ export default class MoreChannels extends React.PureComponent<Props, State> {
|
||||
this.searchTimeoutId = 0;
|
||||
|
||||
this.state = {
|
||||
show: true,
|
||||
shouldShowArchivedChannels: this.props.morePublicChannelsModalType === 'private',
|
||||
search: false,
|
||||
searchedChannels: [],
|
||||
serverError: null,
|
||||
searching: false,
|
||||
searchTerm: '',
|
||||
loading: true,
|
||||
};
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
await this.props.actions.getChannels(this.props.teamId, 0, CHANNELS_CHUNK_SIZE * 2);
|
||||
componentDidMount() {
|
||||
this.props.actions.getChannels(this.props.teamId, 0, CHANNELS_CHUNK_SIZE * 2);
|
||||
if (this.props.canShowArchivedChannels) {
|
||||
await this.props.actions.getArchivedChannels(this.props.teamId, 0, CHANNELS_CHUNK_SIZE * 2);
|
||||
this.props.actions.getArchivedChannels(this.props.teamId, 0, CHANNELS_CHUNK_SIZE * 2);
|
||||
}
|
||||
await this.props.channels.forEach((channel) => this.props.actions.getChannelStats(channel.id));
|
||||
this.loadComplete();
|
||||
}
|
||||
|
||||
loadComplete = () => {
|
||||
this.setState({loading: false});
|
||||
handleHide = () => {
|
||||
this.setState({show: false});
|
||||
}
|
||||
|
||||
handleNewChannel = () => {
|
||||
@@ -145,17 +124,14 @@ export default class MoreChannels extends React.PureComponent<Props, State> {
|
||||
|
||||
handleJoin = async (channel: Channel, done: () => void) => {
|
||||
const {actions, currentUserId, teamId, teamName} = this.props;
|
||||
let result;
|
||||
const result = await actions.joinChannel(currentUserId, teamId, channel.id);
|
||||
|
||||
if (!this.isMemberOfChannel(channel.id)) {
|
||||
result = await actions.joinChannel(currentUserId, teamId, channel.id);
|
||||
}
|
||||
|
||||
if (result?.error) {
|
||||
if (result.error) {
|
||||
this.setState({serverError: result.error.message});
|
||||
} else {
|
||||
getHistory().push(getRelativeChannelURL(teamName, channel.name));
|
||||
this.closeEditRHS();
|
||||
this.handleHide();
|
||||
}
|
||||
|
||||
if (done) {
|
||||
@@ -177,7 +153,7 @@ export default class MoreChannels extends React.PureComponent<Props, State> {
|
||||
const searchTimeoutId = window.setTimeout(
|
||||
async () => {
|
||||
try {
|
||||
const {data} = await this.props.actions.searchMoreChannels(term, this.state.shouldShowArchivedChannels, this.props.shouldHideJoinedChannels);
|
||||
const {data} = await this.props.actions.searchMoreChannels(term, this.state.shouldShowArchivedChannels);
|
||||
if (searchTimeoutId !== this.searchTimeoutId) {
|
||||
return;
|
||||
}
|
||||
@@ -207,47 +183,29 @@ export default class MoreChannels extends React.PureComponent<Props, State> {
|
||||
this.setState({shouldShowArchivedChannels});
|
||||
}
|
||||
|
||||
isMemberOfChannel(channelId: string) {
|
||||
return this.props.myChannelMemberships[channelId];
|
||||
}
|
||||
|
||||
handleShowJoinedChannelsPreference = (shouldHideJoinedChannels: boolean) => {
|
||||
// search again when switching channels to update search results
|
||||
this.search(this.state.searchTerm);
|
||||
this.props.actions.setGlobalItem(StoragePrefixes.HIDE_JOINED_CHANNELS, shouldHideJoinedChannels.toString());
|
||||
}
|
||||
|
||||
otherChannelsWithoutJoined = this.props.channels.filter((channel) => !this.isMemberOfChannel(channel.id));
|
||||
archivedChannelsWithoutJoined = this.props.archivedChannels.filter((channel) => !this.isMemberOfChannel(channel.id));
|
||||
|
||||
render() {
|
||||
const {
|
||||
channels,
|
||||
archivedChannels,
|
||||
teamId,
|
||||
channelsRequestStarted,
|
||||
shouldHideJoinedChannels,
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
search,
|
||||
searchedChannels,
|
||||
serverError: serverErrorState,
|
||||
show,
|
||||
searching,
|
||||
shouldShowArchivedChannels,
|
||||
} = this.state;
|
||||
|
||||
const otherChannelsWithoutJoined = channels.filter((channel) => !this.isMemberOfChannel(channel.id));
|
||||
const archivedChannelsWithoutJoined = archivedChannels.filter((channel) => !this.isMemberOfChannel(channel.id));
|
||||
let activeChannels;
|
||||
|
||||
if (shouldShowArchivedChannels && shouldHideJoinedChannels) {
|
||||
this.activeChannels = search ? searchedChannels : archivedChannelsWithoutJoined;
|
||||
} else if (shouldShowArchivedChannels && !shouldHideJoinedChannels) {
|
||||
this.activeChannels = search ? searchedChannels : archivedChannels;
|
||||
} else if (!shouldShowArchivedChannels && shouldHideJoinedChannels) {
|
||||
this.activeChannels = search ? searchedChannels : otherChannelsWithoutJoined;
|
||||
if (shouldShowArchivedChannels) {
|
||||
activeChannels = search ? searchedChannels : archivedChannels;
|
||||
} else {
|
||||
this.activeChannels = search ? searchedChannels : channels;
|
||||
activeChannels = search ? searchedChannels : channels;
|
||||
}
|
||||
|
||||
let serverError;
|
||||
@@ -256,87 +214,87 @@ export default class MoreChannels extends React.PureComponent<Props, State> {
|
||||
<div className='form-group has-error'><label className='control-label'>{serverErrorState}</label></div>;
|
||||
}
|
||||
|
||||
const createNewChannelButton = (className: string, icon?: JSX.Element) => {
|
||||
const buttonClassName = classNames('btn', className);
|
||||
return (
|
||||
<TeamPermissionGate
|
||||
teamId={teamId}
|
||||
permissions={[Permissions.CREATE_PUBLIC_CHANNEL]}
|
||||
const createNewChannelButton = (
|
||||
<TeamPermissionGate
|
||||
teamId={teamId}
|
||||
permissions={[Permissions.CREATE_PUBLIC_CHANNEL]}
|
||||
>
|
||||
<button
|
||||
id='createNewChannel'
|
||||
type='button'
|
||||
className='btn btn-primary channel-create-btn'
|
||||
onClick={this.handleNewChannel}
|
||||
>
|
||||
<button
|
||||
type='button'
|
||||
id='createNewChannelButton'
|
||||
className={buttonClassName}
|
||||
onClick={this.handleNewChannel}
|
||||
aria-label={localizeMessage('more_channels.create', 'Create New Channel')}
|
||||
>
|
||||
{icon}
|
||||
<FormattedMessage
|
||||
id='more_channels.create'
|
||||
defaultMessage='Create New Channel'
|
||||
/>
|
||||
</button>
|
||||
</TeamPermissionGate>
|
||||
);
|
||||
};
|
||||
|
||||
const noResultsText = (
|
||||
<>
|
||||
<p className='secondary-message'>
|
||||
<FormattedMessage
|
||||
id='more_channels.searchError'
|
||||
defaultMessage='Try searching different keywords, checking for typos or adjusting the filters.'
|
||||
id='more_channels.create'
|
||||
defaultMessage='Create Channel'
|
||||
/>
|
||||
</p>
|
||||
{createNewChannelButton('primaryButton', <i className='icon-plus'/>)}
|
||||
</>
|
||||
</button>
|
||||
</TeamPermissionGate>
|
||||
);
|
||||
|
||||
const body = this.state.loading ? <LoadingScreen/> : (
|
||||
const createChannelHelpText = (
|
||||
<TeamPermissionGate
|
||||
teamId={teamId}
|
||||
permissions={[Permissions.CREATE_PUBLIC_CHANNEL, Permissions.CREATE_PRIVATE_CHANNEL]}
|
||||
>
|
||||
<p className='secondary-message'>
|
||||
<FormattedMessage
|
||||
id='more_channels.createClick'
|
||||
defaultMessage="Click 'Create New Channel' to make a new one"
|
||||
/>
|
||||
</p>
|
||||
</TeamPermissionGate>
|
||||
);
|
||||
|
||||
const body = (
|
||||
<React.Fragment>
|
||||
<SearchableChannelList
|
||||
channels={this.activeChannels}
|
||||
channels={activeChannels}
|
||||
channelsPerPage={CHANNELS_PER_PAGE}
|
||||
nextPage={this.nextPage}
|
||||
isSearch={search}
|
||||
search={this.search}
|
||||
handleJoin={this.handleJoin}
|
||||
noResultsText={noResultsText}
|
||||
noResultsText={createChannelHelpText}
|
||||
loading={search ? searching : channelsRequestStarted}
|
||||
toggleArchivedChannels={this.toggleArchivedChannels}
|
||||
shouldShowArchivedChannels={this.state.shouldShowArchivedChannels}
|
||||
canShowArchivedChannels={this.props.canShowArchivedChannels}
|
||||
myChannelMemberships={this.props.myChannelMemberships}
|
||||
allChannelStats={this.props.allChannelStats}
|
||||
closeModal={this.props.actions.closeModal}
|
||||
hideJoinedChannelsPreference={this.handleShowJoinedChannelsPreference}
|
||||
rememberHideJoinedChannelsChecked={shouldHideJoinedChannels}
|
||||
/>
|
||||
{serverError}
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
const title = (
|
||||
<FormattedMessage
|
||||
id='more_channels.title'
|
||||
defaultMessage='Browse Channels'
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<GenericModal
|
||||
<Modal
|
||||
dialogClassName='a11y__modal more-modal more-modal--action'
|
||||
show={show}
|
||||
onHide={this.handleHide}
|
||||
onExited={this.handleExit}
|
||||
compassDesign={true}
|
||||
role='dialog'
|
||||
id='moreChannelsModal'
|
||||
aria-labelledby='moreChannelsModalLabel'
|
||||
modalHeaderText={title}
|
||||
headerButton={createNewChannelButton('outlineButton')}
|
||||
autoCloseOnConfirmButton={false}
|
||||
aria-modal={true}
|
||||
enforceFocus={false}
|
||||
>
|
||||
{body}
|
||||
</GenericModal>
|
||||
<Modal.Header
|
||||
id='moreChannelsModalHeader'
|
||||
closeButton={true}
|
||||
>
|
||||
<Modal.Title
|
||||
componentClass='h1'
|
||||
id='moreChannelsModalLabel'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='more_channels.title'
|
||||
defaultMessage='More Channels'
|
||||
/>
|
||||
</Modal.Title>
|
||||
{createNewChannelButton}
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
{body}
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,483 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import {AccountOutlineIcon, ArchiveOutlineIcon, CheckIcon, ChevronDownIcon, GlobeIcon, LockOutlineIcon, MagnifyIcon} from '@mattermost/compass-icons/components';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import {isPrivateChannel} from 'mattermost-redux/utils/channel_utils';
|
||||
|
||||
import LoadingScreen from 'components/loading_screen';
|
||||
import LoadingWrapper from 'components/widgets/loading/loading_wrapper';
|
||||
import QuickInput from 'components/quick_input';
|
||||
import LocalizedInput from 'components/localized_input/localized_input';
|
||||
import CheckboxCheckedIcon from 'components/widgets/icons/checkbox_checked_icon';
|
||||
import MagnifyingGlassSVG from 'components/common/svg_images_components/magnifying_glass_svg';
|
||||
import MenuWrapper from 'components/widgets/menu/menu_wrapper';
|
||||
import Menu from 'components/widgets/menu/menu';
|
||||
|
||||
import {t} from 'utils/i18n';
|
||||
import * as UserAgent from 'utils/user_agent';
|
||||
import Constants, {ModalIdentifiers} from 'utils/constants';
|
||||
import {isKeyPressed, localizeMessage, localizeAndFormatMessage} from 'utils/utils';
|
||||
|
||||
import {isArchivedChannel} from 'utils/channel_utils';
|
||||
|
||||
const NEXT_BUTTON_TIMEOUT_MILLISECONDS = 500;
|
||||
|
||||
export default class SearchableChannelList extends React.PureComponent {
|
||||
static getDerivedStateFromProps(props, state) {
|
||||
return {isSearch: props.isSearch, page: props.isSearch && !state.isSearch ? 0 : state.page};
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.nextTimeoutId = 0;
|
||||
|
||||
this.state = {
|
||||
joiningChannel: '',
|
||||
page: 0,
|
||||
nextDisabled: false,
|
||||
channelSearchValue: '',
|
||||
};
|
||||
|
||||
this.filter = React.createRef();
|
||||
this.channelListScroll = React.createRef();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// only focus the search box on desktop so that we don't cause the keyboard to open on mobile
|
||||
if (!UserAgent.isMobile() && this.filter.current) {
|
||||
this.filter.current.focus();
|
||||
}
|
||||
document.addEventListener('keydown', this.onKeyDown);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('keydown', this.onKeyDown);
|
||||
}
|
||||
|
||||
onKeyDown = (e) => {
|
||||
const target = e.target;
|
||||
const isEnterKeyPressed = isKeyPressed(e, Constants.KeyCodes.ENTER);
|
||||
if (isEnterKeyPressed && (e.shiftKey || e.ctrlKey || e.altKey)) {
|
||||
return;
|
||||
}
|
||||
if (isEnterKeyPressed && target.classList.contains('more-modal__row')) {
|
||||
target.click();
|
||||
}
|
||||
}
|
||||
|
||||
handleJoin = (channel, e) => {
|
||||
e.stopPropagation();
|
||||
this.setState({joiningChannel: channel.id});
|
||||
this.props.handleJoin(
|
||||
channel,
|
||||
() => {
|
||||
this.setState({joiningChannel: ''});
|
||||
},
|
||||
);
|
||||
if (this.isMemberOfChannel(channel.id)) {
|
||||
this.props.closeModal(ModalIdentifiers.MORE_CHANNELS);
|
||||
}
|
||||
}
|
||||
|
||||
isMemberOfChannel(channelId) {
|
||||
return this.props.myChannelMemberships[channelId];
|
||||
}
|
||||
|
||||
createChannelRow = (channel) => {
|
||||
const ariaLabel = `${channel.display_name}, ${channel.purpose}`.toLowerCase();
|
||||
let channelTypeIcon;
|
||||
|
||||
let memberCount = 0;
|
||||
if (this.props.allChannelStats[channel.id]) {
|
||||
memberCount = this.props.allChannelStats[channel.id].member_count;
|
||||
}
|
||||
|
||||
if (isArchivedChannel(channel)) {
|
||||
channelTypeIcon = <ArchiveOutlineIcon size={18}/>;
|
||||
} else if (isPrivateChannel(channel)) {
|
||||
channelTypeIcon = <LockOutlineIcon size={18}/>;
|
||||
} else {
|
||||
channelTypeIcon = <GlobeIcon size={18}/>;
|
||||
}
|
||||
|
||||
const membershipIndicator = this.isMemberOfChannel(channel.id) ? (
|
||||
<div
|
||||
id='membershipIndicatorContainer'
|
||||
aria-label={localizeMessage('more_channels.membership_indicator', 'Membership Indicator: Joined')}
|
||||
>
|
||||
<CheckIcon size={14}/>
|
||||
<FormattedMessage
|
||||
id={'more_channels.joined'}
|
||||
defaultMessage={'Joined'}
|
||||
/>
|
||||
<span className='dot'/>
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const channelPurposeContainerAriaLabel = localizeAndFormatMessage(
|
||||
t('more_channels.channel_purpose'),
|
||||
'Channel Information: Membership Indicator: Joined, Member count {memberCount} , Purpose: {channelPurpose}',
|
||||
{memberCount, channelPurpose: channel.purpose || ''},
|
||||
);
|
||||
|
||||
const channelPurposeContainer = (
|
||||
<div
|
||||
id='channelPurposeContainer'
|
||||
aria-label={channelPurposeContainerAriaLabel}
|
||||
>
|
||||
{membershipIndicator}
|
||||
<AccountOutlineIcon size={14}/>
|
||||
<span>{memberCount}</span>
|
||||
{channel.purpose.length > 0 && <span className='dot'/>}
|
||||
<span className='more-modal__description'>{channel.purpose}</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
const joinViewChannelButtonClass = classNames('btn', {
|
||||
outlineButton: this.isMemberOfChannel(channel.id),
|
||||
primaryButton: !this.isMemberOfChannel(channel.id),
|
||||
});
|
||||
|
||||
const joinViewChannelButton = (
|
||||
<button
|
||||
id='joinViewChannelButton'
|
||||
onClick={(e) => this.handleJoin(channel, e)}
|
||||
className={joinViewChannelButtonClass}
|
||||
disabled={this.state.joiningChannel}
|
||||
tabIndex={-1}
|
||||
aria-label={this.isMemberOfChannel(channel.id) ? localizeMessage('more_channels.view', 'View') : localizeMessage('joinChannel.JoinButton', 'Join')}
|
||||
>
|
||||
<LoadingWrapper
|
||||
loading={this.state.joiningChannel === channel.id}
|
||||
text={localizeMessage('joinChannel.joiningButton', 'Joining...')}
|
||||
>
|
||||
<FormattedMessage
|
||||
id={this.isMemberOfChannel(channel.id) ? 'more_channels.view' : 'joinChannel.JoinButton'}
|
||||
defaultMessage={this.isMemberOfChannel(channel.id) ? 'View' : 'Join'}
|
||||
/>
|
||||
</LoadingWrapper>
|
||||
</button>
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className='more-modal__row'
|
||||
key={channel.id}
|
||||
id={`ChannelRow-${channel.name}`}
|
||||
aria-label={ariaLabel}
|
||||
onClick={(e) => this.handleJoin(channel, e)}
|
||||
tabIndex={0}
|
||||
>
|
||||
<div className='more-modal__details'>
|
||||
<div className='style--none more-modal__name'>
|
||||
{channelTypeIcon}
|
||||
<span id='channelName'>{channel.display_name}</span>
|
||||
</div>
|
||||
{channelPurposeContainer}
|
||||
</div>
|
||||
<div className='more-modal__actions'>
|
||||
{joinViewChannelButton}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
nextPage = (e) => {
|
||||
e.preventDefault();
|
||||
this.setState({page: this.state.page + 1, nextDisabled: true});
|
||||
this.nextTimeoutId = setTimeout(() => this.setState({nextDisabled: false}), NEXT_BUTTON_TIMEOUT_MILLISECONDS);
|
||||
this.props.nextPage(this.state.page + 1);
|
||||
this.channelListScroll.current?.scrollTo({top: 0});
|
||||
}
|
||||
|
||||
previousPage = (e) => {
|
||||
e.preventDefault();
|
||||
this.setState({page: this.state.page - 1});
|
||||
this.channelListScroll.current?.scrollTo({top: 0});
|
||||
}
|
||||
|
||||
doSearch = () => {
|
||||
this.props.search(this.state.channelSearchValue);
|
||||
if (this.state.channelSearchValue === '') {
|
||||
this.setState({page: 0});
|
||||
}
|
||||
}
|
||||
|
||||
handleChange = (e) => {
|
||||
if (e.target) {
|
||||
this.setState({channelSearchValue: e.target.value}, () => this.doSearch());
|
||||
}
|
||||
}
|
||||
|
||||
handleClear = () => {
|
||||
this.setState({channelSearchValue: ''}, () => this.doSearch());
|
||||
}
|
||||
|
||||
toggleArchivedChannelsOn = () => {
|
||||
this.props.toggleArchivedChannels(true);
|
||||
}
|
||||
|
||||
toggleArchivedChannelsOff = () => {
|
||||
this.props.toggleArchivedChannels(false);
|
||||
}
|
||||
|
||||
handleChecked = () => {
|
||||
// If it was checked, and now we're unchecking it, clear the preference
|
||||
if (this.props.rememberHideJoinedChannelsChecked) {
|
||||
this.props.hideJoinedChannelsPreference(false);
|
||||
} else {
|
||||
this.props.hideJoinedChannelsPreference(true);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const channels = this.props.channels;
|
||||
let listContent;
|
||||
let nextButton;
|
||||
let previousButton;
|
||||
|
||||
let emptyStateMessage = (
|
||||
<FormattedMessage
|
||||
id={this.props.shouldShowArchivedChannels ? t('more_channels.noArchived') : t('more_channels.noPublic')}
|
||||
tagName='strong'
|
||||
defaultMessage={this.props.shouldShowArchivedChannels ? 'No archived channels' : 'No public channels'}
|
||||
/>
|
||||
);
|
||||
|
||||
if (this.state.channelSearchValue.length > 0) {
|
||||
emptyStateMessage = (
|
||||
<FormattedMessage
|
||||
id='more_channels.noMore'
|
||||
tagName='strong'
|
||||
defaultMessage='No results for {text}'
|
||||
values={{
|
||||
text: this.state.channelSearchValue,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.loading && channels.length === 0) {
|
||||
listContent = <LoadingScreen/>;
|
||||
} else if (channels.length === 0) {
|
||||
listContent = (
|
||||
<div
|
||||
className='no-channel-message'
|
||||
aria-label={this.state.channelSearchValue.length > 0 ?
|
||||
localizeAndFormatMessage(t('more_channels.noMore'), 'No results for {text}', {text: this.state.channelSearchValue}) :
|
||||
localizeMessage('widgets.channels_input.empty', 'No channels found')
|
||||
}
|
||||
>
|
||||
<MagnifyingGlassSVG/>
|
||||
<h3 className='primary-message'>
|
||||
{emptyStateMessage}
|
||||
</h3>
|
||||
{this.props.noResultsText}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
const pageStart = this.state.page * this.props.channelsPerPage;
|
||||
const pageEnd = pageStart + this.props.channelsPerPage;
|
||||
const channelsToDisplay = this.props.channels.slice(pageStart, pageEnd);
|
||||
listContent = channelsToDisplay.map(this.createChannelRow);
|
||||
|
||||
if (channelsToDisplay.length >= this.props.channelsPerPage && pageEnd < this.props.channels.length) {
|
||||
nextButton = (
|
||||
<button
|
||||
className='btn filter-control filter-control__next outlineButton'
|
||||
onClick={this.nextPage}
|
||||
disabled={this.state.nextDisabled}
|
||||
aria-label={localizeMessage('more_channels.next', 'Next')}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='more_channels.next'
|
||||
defaultMessage='Next'
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.page > 0) {
|
||||
previousButton = (
|
||||
<button
|
||||
className='btn filter-control filter-control__prev outlineButton'
|
||||
onClick={this.previousPage}
|
||||
aria-label={localizeMessage('more_channels.prev', 'Previous')}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='more_channels.prev'
|
||||
defaultMessage='Previous'
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const input = (
|
||||
<div className='filter-row filter-row--full'>
|
||||
<span
|
||||
id='searchIcon'
|
||||
aria-hidden='true'
|
||||
>
|
||||
<MagnifyIcon size={18}/>
|
||||
</span>
|
||||
<QuickInput
|
||||
id='searchChannelsTextbox'
|
||||
ref={this.filter}
|
||||
className='form-control filter-textbox'
|
||||
placeholder={{id: t('filtered_channels_list.search'), defaultMessage: 'Search channels'}}
|
||||
inputComponent={LocalizedInput}
|
||||
onInput={this.handleChange}
|
||||
clearable={true}
|
||||
onClear={this.handleClear}
|
||||
value={this.state.channelSearchValue}
|
||||
aria-label={localizeMessage('filtered_channels_list.search', 'Search Channels')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
let channelDropdown;
|
||||
let checkIcon;
|
||||
|
||||
if (this.props.canShowArchivedChannels) {
|
||||
checkIcon = (
|
||||
<CheckIcon
|
||||
size={18}
|
||||
color={'var(--button-bg)'}
|
||||
/>
|
||||
);
|
||||
channelDropdown = (
|
||||
<MenuWrapper id='channelsMoreDropdown'>
|
||||
<a id='menuWrapper'>
|
||||
<span>{this.props.shouldShowArchivedChannels ? localizeMessage('more_channels.show_archived_channels', 'Channel Type: Archived') : localizeMessage('more_channels.show_public_channels', 'Channel Type: Public')}</span>
|
||||
<ChevronDownIcon
|
||||
color={'rgba(var(--center-channel-color-rgb), 0.64)'}
|
||||
size={16}
|
||||
/>
|
||||
</a>
|
||||
<Menu
|
||||
openLeft={false}
|
||||
ariaLabel={localizeMessage('more_channels.title', 'Browse channels')}
|
||||
>
|
||||
<div id='modalPreferenceContainer'>
|
||||
<Menu.ItemAction
|
||||
id='channelsMoreDropdownPublic'
|
||||
onClick={this.toggleArchivedChannelsOff}
|
||||
icon={<GlobeIcon size={16}/>}
|
||||
text={localizeMessage('suggestion.search.public', 'Public Channels')}
|
||||
rightDecorator={this.props.shouldShowArchivedChannels ? null : checkIcon}
|
||||
ariaLabel={localizeMessage('suggestion.search.public', 'Public Channels')}
|
||||
/>
|
||||
</div>
|
||||
<Menu.ItemAction
|
||||
id='channelsMoreDropdownArchived'
|
||||
onClick={this.toggleArchivedChannelsOn}
|
||||
icon={<ArchiveOutlineIcon size={16}/>}
|
||||
text={localizeMessage('suggestion.archive', 'Archived Channels')}
|
||||
rightDecorator={this.props.shouldShowArchivedChannels ? checkIcon : null}
|
||||
ariaLabel={localizeMessage('suggestion.archive', 'Archived Channels')}
|
||||
/>
|
||||
</Menu>
|
||||
</MenuWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
const hideJoinedButtonClass = classNames('get-app__checkbox', {checked: this.props.rememberHideJoinedChannelsChecked});
|
||||
const hideJoinedPreferenceCheckbox = (
|
||||
<div
|
||||
id={'hideJoinedPreferenceCheckbox'}
|
||||
onClick={this.handleChecked}
|
||||
>
|
||||
<button
|
||||
className={hideJoinedButtonClass}
|
||||
aria-label={this.props.rememberHideJoinedChannelsChecked ? localizeMessage('more_channels.hide_joined_checked', 'Hide joined channels checkbox, checked') : localizeMessage('more_channels.hide_joined_not_checked', 'Hide joined channels checkbox, not checked')
|
||||
}
|
||||
>
|
||||
{this.props.rememberHideJoinedChannelsChecked ? <CheckboxCheckedIcon/> : null}
|
||||
</button>
|
||||
<FormattedMessage
|
||||
id='more_channels.hide_joined'
|
||||
defaultMessage='Hide Joined'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
let channelCountLabel;
|
||||
if (channels.length === 0) {
|
||||
channelCountLabel = localizeMessage('more_channels.count_zero', '0 Results');
|
||||
} else if (channels.length === 1) {
|
||||
channelCountLabel = localizeMessage('more_channels.count_one', '1 Result');
|
||||
} else if (channels.length > 1) {
|
||||
channelCountLabel = localizeAndFormatMessage(t('more_channels.count'), '0 Results', {count: channels.length});
|
||||
} else {
|
||||
channelCountLabel = localizeMessage('more_channels.count_zero', '0 Results');
|
||||
}
|
||||
|
||||
const dropDownContainer = (
|
||||
<div className='more-modal__dropdown'>
|
||||
<span id='channelCountLabel'>{channelCountLabel}</span>
|
||||
<div id='modalPreferenceContainer'>
|
||||
{channelDropdown}
|
||||
{hideJoinedPreferenceCheckbox}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className='filtered-user-list'>
|
||||
{input}
|
||||
{dropDownContainer}
|
||||
<div
|
||||
role='application'
|
||||
className='more-modal__list'
|
||||
tabIndex={-1}
|
||||
>
|
||||
<div
|
||||
id='moreChannelsList'
|
||||
tabIndex={-1}
|
||||
ref={this.channelListScroll}
|
||||
>
|
||||
{listContent}
|
||||
</div>
|
||||
</div>
|
||||
<div className='filter-controls'>
|
||||
{previousButton}
|
||||
{nextButton}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SearchableChannelList.defaultProps = {
|
||||
channels: [],
|
||||
isSearch: false,
|
||||
};
|
||||
|
||||
SearchableChannelList.propTypes = {
|
||||
channels: PropTypes.arrayOf(PropTypes.object),
|
||||
channelsPerPage: PropTypes.number,
|
||||
nextPage: PropTypes.func.isRequired,
|
||||
isSearch: PropTypes.bool,
|
||||
search: PropTypes.func.isRequired,
|
||||
handleJoin: PropTypes.func.isRequired,
|
||||
noResultsText: PropTypes.object,
|
||||
loading: PropTypes.bool,
|
||||
toggleArchivedChannels: PropTypes.func.isRequired,
|
||||
shouldShowArchivedChannels: PropTypes.bool.isRequired,
|
||||
canShowArchivedChannels: PropTypes.bool.isRequired,
|
||||
myChannelMemberships: PropTypes.object.isRequired,
|
||||
allChannelStats: PropTypes.object.isRequired,
|
||||
closeModal: PropTypes.func.isRequired,
|
||||
hideJoinedChannelsPreference: PropTypes.func.isRequired,
|
||||
rememberHideJoinedChannelsChecked: PropTypes.bool.isRequired,
|
||||
};
|
||||
/* eslint-enable react/no-string-refs */
|
||||
319
webapp/channels/src/components/searchable_channel_list.jsx
Normal file
319
webapp/channels/src/components/searchable_channel_list.jsx
Normal file
@@ -0,0 +1,319 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import {ArchiveOutlineIcon} from '@mattermost/compass-icons/components';
|
||||
|
||||
import LoadingScreen from 'components/loading_screen';
|
||||
import LoadingWrapper from 'components/widgets/loading/loading_wrapper';
|
||||
import QuickInput from 'components/quick_input';
|
||||
import * as UserAgent from 'utils/user_agent';
|
||||
import {localizeMessage} from 'utils/utils';
|
||||
import LocalizedInput from 'components/localized_input/localized_input';
|
||||
|
||||
import SharedChannelIndicator from 'components/shared_channel_indicator';
|
||||
|
||||
import {t} from 'utils/i18n';
|
||||
|
||||
import MenuWrapper from './widgets/menu/menu_wrapper';
|
||||
import Menu from './widgets/menu/menu';
|
||||
|
||||
const NEXT_BUTTON_TIMEOUT_MILLISECONDS = 500;
|
||||
|
||||
export default class SearchableChannelList extends React.PureComponent {
|
||||
static getDerivedStateFromProps(props, state) {
|
||||
return {isSearch: props.isSearch, page: props.isSearch && !state.isSearch ? 0 : state.page};
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.nextTimeoutId = 0;
|
||||
|
||||
this.state = {
|
||||
joiningChannel: '',
|
||||
page: 0,
|
||||
nextDisabled: false,
|
||||
};
|
||||
|
||||
this.filter = React.createRef();
|
||||
this.channelListScroll = React.createRef();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// only focus the search box on desktop so that we don't cause the keyboard to open on mobile
|
||||
if (!UserAgent.isMobile() && this.filter.current) {
|
||||
this.filter.current.focus();
|
||||
}
|
||||
}
|
||||
|
||||
handleJoin(channel) {
|
||||
this.setState({joiningChannel: channel.id});
|
||||
this.props.handleJoin(
|
||||
channel,
|
||||
() => {
|
||||
this.setState({joiningChannel: ''});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
createChannelRow = (channel) => {
|
||||
const ariaLabel = `${channel.display_name}, ${channel.purpose}`.toLowerCase();
|
||||
let archiveIcon;
|
||||
let sharedIcon;
|
||||
const {shouldShowArchivedChannels} = this.props;
|
||||
|
||||
if (shouldShowArchivedChannels) {
|
||||
archiveIcon = (
|
||||
<ArchiveOutlineIcon
|
||||
size={20}
|
||||
color={'currentColor'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (channel.shared) {
|
||||
sharedIcon = (
|
||||
<SharedChannelIndicator
|
||||
className='shared-channel-icon'
|
||||
channelType={channel.type}
|
||||
withTooltip={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className='more-modal__row'
|
||||
key={channel.id}
|
||||
id={`ChannelRow-${channel.name}`}
|
||||
>
|
||||
<div className='more-modal__details'>
|
||||
<button
|
||||
onClick={this.handleJoin.bind(this, channel)}
|
||||
aria-label={ariaLabel}
|
||||
className='style--none more-modal__name'
|
||||
>
|
||||
{archiveIcon}
|
||||
{channel.display_name}
|
||||
{sharedIcon}
|
||||
</button>
|
||||
<p className='more-modal__description'>{channel.purpose}</p>
|
||||
</div>
|
||||
<div className='more-modal__actions'>
|
||||
<button
|
||||
onClick={this.handleJoin.bind(this, channel)}
|
||||
className='btn btn-primary'
|
||||
disabled={this.state.joiningChannel}
|
||||
>
|
||||
<LoadingWrapper
|
||||
loading={this.state.joiningChannel === channel.id}
|
||||
text={localizeMessage('more_channels.joining', 'Joining...')}
|
||||
>
|
||||
<FormattedMessage
|
||||
id={shouldShowArchivedChannels ? t('more_channels.view') : t('more_channels.join')}
|
||||
defaultMessage={shouldShowArchivedChannels ? 'View' : 'Join'}
|
||||
/>
|
||||
</LoadingWrapper>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
nextPage = (e) => {
|
||||
e.preventDefault();
|
||||
this.setState({page: this.state.page + 1, nextDisabled: true});
|
||||
this.nextTimeoutId = setTimeout(() => this.setState({nextDisabled: false}), NEXT_BUTTON_TIMEOUT_MILLISECONDS);
|
||||
this.props.nextPage(this.state.page + 1);
|
||||
this.channelListScroll.current?.scrollTo({top: 0});
|
||||
}
|
||||
|
||||
previousPage = (e) => {
|
||||
e.preventDefault();
|
||||
this.setState({page: this.state.page - 1});
|
||||
this.channelListScroll.current?.scrollTo({top: 0});
|
||||
}
|
||||
|
||||
doSearch = () => {
|
||||
const term = this.filter.current.value;
|
||||
this.props.search(term);
|
||||
if (term === '') {
|
||||
this.setState({page: 0});
|
||||
}
|
||||
}
|
||||
toggleArchivedChannelsOn = () => {
|
||||
this.props.toggleArchivedChannels(true);
|
||||
}
|
||||
toggleArchivedChannelsOff = () => {
|
||||
this.props.toggleArchivedChannels(false);
|
||||
}
|
||||
|
||||
render() {
|
||||
const channels = this.props.channels;
|
||||
let listContent;
|
||||
let nextButton;
|
||||
let previousButton;
|
||||
|
||||
if (this.props.loading && channels.length === 0) {
|
||||
listContent = <LoadingScreen/>;
|
||||
} else if (channels.length === 0) {
|
||||
listContent = (
|
||||
<div className='no-channel-message'>
|
||||
<h3 className='primary-message'>
|
||||
<FormattedMessage
|
||||
id='more_channels.noMore'
|
||||
tagName='strong'
|
||||
defaultMessage='No more channels to join'
|
||||
/>
|
||||
</h3>
|
||||
{this.props.noResultsText}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
const pageStart = this.state.page * this.props.channelsPerPage;
|
||||
const pageEnd = pageStart + this.props.channelsPerPage;
|
||||
const channelsToDisplay = this.props.channels.slice(pageStart, pageEnd);
|
||||
listContent = channelsToDisplay.map(this.createChannelRow);
|
||||
|
||||
if (channelsToDisplay.length >= this.props.channelsPerPage && pageEnd < this.props.channels.length) {
|
||||
nextButton = (
|
||||
<button
|
||||
className='btn btn-link filter-control filter-control__next'
|
||||
onClick={this.nextPage}
|
||||
disabled={this.state.nextDisabled}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='more_channels.next'
|
||||
defaultMessage='Next'
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.page > 0) {
|
||||
previousButton = (
|
||||
<button
|
||||
className='btn btn-link filter-control filter-control__prev'
|
||||
onClick={this.previousPage}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='more_channels.prev'
|
||||
defaultMessage='Previous'
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let input = (
|
||||
<div className='filter-row filter-row--full'>
|
||||
<div className='col-sm-12'>
|
||||
<QuickInput
|
||||
id='searchChannelsTextbox'
|
||||
ref={this.filter}
|
||||
className='form-control filter-textbox'
|
||||
placeholder={{id: t('filtered_channels_list.search'), defaultMessage: 'Search channels'}}
|
||||
inputComponent={LocalizedInput}
|
||||
onInput={this.doSearch}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
if (this.props.createChannelButton) {
|
||||
input = (
|
||||
<div className='channel_search'>
|
||||
<div className='search_input'>
|
||||
<QuickInput
|
||||
id='searchChannelsTextbox'
|
||||
ref={this.filter}
|
||||
className='form-control filter-textbox'
|
||||
placeholder={{id: t('filtered_channels_list.search'), defaultMessage: 'Search channels'}}
|
||||
inputComponent={LocalizedInput}
|
||||
onInput={this.doSearch}
|
||||
/>
|
||||
</div>
|
||||
<div className='create_button'>
|
||||
{this.props.createChannelButton}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let channelDropdown;
|
||||
|
||||
if (this.props.canShowArchivedChannels) {
|
||||
channelDropdown = (
|
||||
<div className='more-modal__dropdown'>
|
||||
<MenuWrapper id='channelsMoreDropdown'>
|
||||
<a>
|
||||
<span>{this.props.shouldShowArchivedChannels ? localizeMessage('more_channels.show_archived_channels', 'Show: Archived Channels') : localizeMessage('more_channels.show_public_channels', 'Show: Public Channels')}</span>
|
||||
<span className='caret'/>
|
||||
</a>
|
||||
<Menu
|
||||
openLeft={false}
|
||||
ariaLabel={localizeMessage('team_members_dropdown.menuAriaLabel', 'Change the role of a team member')}
|
||||
>
|
||||
<Menu.ItemAction
|
||||
id='channelsMoreDropdownPublic'
|
||||
onClick={this.toggleArchivedChannelsOff}
|
||||
text={localizeMessage('suggestion.search.public', 'Public Channels')}
|
||||
/>
|
||||
<Menu.ItemAction
|
||||
id='channelsMoreDropdownArchived'
|
||||
onClick={this.toggleArchivedChannelsOn}
|
||||
text={localizeMessage('suggestion.archive', 'Archived Channels')}
|
||||
/>
|
||||
</Menu>
|
||||
</MenuWrapper>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='filtered-user-list'>
|
||||
{input}
|
||||
{channelDropdown}
|
||||
<div
|
||||
role='application'
|
||||
className='more-modal__list'
|
||||
>
|
||||
<div
|
||||
id='moreChannelsList'
|
||||
ref={this.channelListScroll}
|
||||
>
|
||||
{listContent}
|
||||
</div>
|
||||
</div>
|
||||
<div className='filter-controls'>
|
||||
{previousButton}
|
||||
{nextButton}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SearchableChannelList.defaultProps = {
|
||||
channels: [],
|
||||
isSearch: false,
|
||||
};
|
||||
|
||||
SearchableChannelList.propTypes = {
|
||||
channels: PropTypes.arrayOf(PropTypes.object),
|
||||
channelsPerPage: PropTypes.number,
|
||||
nextPage: PropTypes.func.isRequired,
|
||||
isSearch: PropTypes.bool,
|
||||
search: PropTypes.func.isRequired,
|
||||
handleJoin: PropTypes.func.isRequired,
|
||||
noResultsText: PropTypes.object,
|
||||
loading: PropTypes.bool,
|
||||
createChannelButton: PropTypes.element,
|
||||
toggleArchivedChannels: PropTypes.func.isRequired,
|
||||
shouldShowArchivedChannels: PropTypes.bool.isRequired,
|
||||
canShowArchivedChannels: PropTypes.bool.isRequired,
|
||||
};
|
||||
@@ -4,25 +4,20 @@
|
||||
import React from 'react';
|
||||
import {shallow} from 'enzyme';
|
||||
|
||||
import SearchableChannelList from './searchable_channel_list.jsx';
|
||||
import SearchableChannelList from 'components/searchable_channel_list.jsx';
|
||||
|
||||
describe('components/SearchableChannelList', () => {
|
||||
const baseProps = {
|
||||
channels: [],
|
||||
isSearch: false,
|
||||
channelsPerPage: 10,
|
||||
nextPage: jest.fn(),
|
||||
search: jest.fn(),
|
||||
handleJoin: jest.fn(),
|
||||
nextPage: () => {}, // eslint-disable-line no-empty-function
|
||||
search: () => {}, // eslint-disable-line no-empty-function
|
||||
handleJoin: () => {}, // eslint-disable-line no-empty-function
|
||||
loading: true,
|
||||
rememberHideJoinedChannelsChecked: false,
|
||||
toggleArchivedChannels: jest.fn(),
|
||||
toggleArchivedChannels: () => {}, // eslint-disable-line no-empty-function
|
||||
shouldShowArchivedChannels: false,
|
||||
canShowArchivedChannels: false,
|
||||
myChannelMemberships: {},
|
||||
allChannelStats: {},
|
||||
closeModal: jest.fn(),
|
||||
hideJoinedChannelsPreference: jest.fn(),
|
||||
};
|
||||
|
||||
test('should match init snapshot', () => {
|
||||
@@ -4155,22 +4155,13 @@
|
||||
"modal.manual_status.title_dnd": "Your Status is Set to \"Do Not Disturb\"",
|
||||
"modal.manual_status.title_offline": "Your Status is Set to \"Offline\"",
|
||||
"modal.manual_status.title_ooo": "Your Status is Set to \"Out of Office\"",
|
||||
"more_channels.channel_purpose": "Channel Information: Membership Indicator: Joined, Member count {memberCount} , Purpose: {channelPurpose}",
|
||||
"more_channels.count": "{count} Results",
|
||||
"more_channels.count_one": "1 Result",
|
||||
"more_channels.count_zero": "0 Results",
|
||||
"more_channels.create": "Create New Channel",
|
||||
"more_channels.hide_joined": "Hide Joined",
|
||||
"more_channels.hide_joined_checked": "Hide joined channels checkbox, checked",
|
||||
"more_channels.hide_joined_not_checked": "Hide joined channels checkbox, not checked",
|
||||
"more_channels.joined": "Joined",
|
||||
"more_channels.membership_indicator": "Membership Indicator: Joined",
|
||||
"more_channels.createClick": "Click 'Create New Channel' to make a new one",
|
||||
"more_channels.join": "Join",
|
||||
"more_channels.joining": "Joining...",
|
||||
"more_channels.next": "Next",
|
||||
"more_channels.noArchived": "No archived channels",
|
||||
"more_channels.noMore": "No results for \"{text}\"",
|
||||
"more_channels.noPublic": "No public channels",
|
||||
"more_channels.prev": "Previous",
|
||||
"more_channels.searchError": "Try searching different keywords, checking for typos or adjusting the filters.",
|
||||
"more_channels.show_archived_channels": "Channel Type: Archived",
|
||||
"more_channels.show_public_channels": "Channel Type: Public",
|
||||
"more_channels.title": "Browse Channels",
|
||||
|
||||
@@ -71,9 +71,7 @@
|
||||
|
||||
.primary-message {
|
||||
margin: 0;
|
||||
color: var(--center-channel-color);
|
||||
font-size: inherit;
|
||||
line-height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -896,7 +896,6 @@ export const StoragePrefixes = {
|
||||
CHANNEL_CATEGORY_COLLAPSED: 'channelCategoryCollapsed_',
|
||||
INLINE_IMAGE_VISIBLE: 'isInlineImageVisible_',
|
||||
DELINQUENCY: 'delinquency_',
|
||||
HIDE_JOINED_CHANNELS: 'hideJoinedChannels',
|
||||
};
|
||||
|
||||
export const LandingPreferenceTypes = {
|
||||
|
||||
@@ -38,7 +38,6 @@ export type Props = {
|
||||
compassDesign?: boolean;
|
||||
backdrop?: boolean;
|
||||
backdropClassName?: string;
|
||||
headerButton?: React.ReactNode;
|
||||
tabIndex?: number;
|
||||
children: React.ReactNode;
|
||||
keyboardEscape?: boolean;
|
||||
@@ -166,7 +165,6 @@ export class GenericModal extends React.PureComponent<Props, State> {
|
||||
<h1 id='genericModalLabel'>
|
||||
{this.props.modalHeaderText}
|
||||
</h1>
|
||||
{this.props.headerButton}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user