mirror of
https://github.com/mattermost/mattermost.git
synced 2026-02-18 18:18:23 -05:00
MM-66442 - Add empty state message for ABAC channel modal (#34965)
* MM-66442 - Add empty state message for ABAC channel modal * fix translation and ts issue * Remove empty filter controls border in channel modal * extract and memoize message * adjust modal header title style and text color --------- Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
parent
cef5134865
commit
5efc41c1bb
6 changed files with 118 additions and 6 deletions
|
|
@ -2,7 +2,7 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import React, {useState, useEffect} from 'react';
|
||||
import React, {useState, useEffect, useMemo} from 'react';
|
||||
import {FormattedMessage, useIntl} from 'react-intl';
|
||||
|
||||
import {GenericModal} from '@mattermost/components';
|
||||
|
|
@ -96,6 +96,21 @@ function PolicyDetails({
|
|||
|
||||
const abacActions = useChannelAccessControlActions();
|
||||
|
||||
// Memoize the custom no options message to avoid recreating it on every render
|
||||
const customNoPrivateChannelsMessage = useMemo(() => (
|
||||
<div
|
||||
key='no-private-channels'
|
||||
className='no-channel-message'
|
||||
>
|
||||
<p className='primary-message'>
|
||||
<FormattedMessage
|
||||
id='admin.access_control.policy.edit_policy.no_private_channels'
|
||||
defaultMessage='There are no private channels available to add to this policy.'
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
), []);
|
||||
|
||||
// Check if there are any usable attributes for ABAC
|
||||
const noUsableAttributes = attributesLoaded && !hasUsableAttributes(autocompleteResult, accessControlSettings.EnableUserManagedAttributes);
|
||||
|
||||
|
|
@ -603,6 +618,7 @@ function PolicyDetails({
|
|||
groupID={''}
|
||||
alreadySelected={Object.values(channelChanges.added).map((channel) => channel.id)}
|
||||
excludeTypes={['O', 'D', 'G']}
|
||||
customNoOptionsMessage={customNoPrivateChannelsMessage}
|
||||
excludeGroupConstrained={true}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -66,6 +66,92 @@ describe('components/ChannelSelectorModal', () => {
|
|||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should show custom no options message when no channels and no search term', () => {
|
||||
const customMessage = (
|
||||
<div className='custom-message'>
|
||||
{'No private channels available'}
|
||||
</div>
|
||||
);
|
||||
|
||||
const wrapper = shallowWithIntl(
|
||||
<ChannelSelectorModal
|
||||
{...defaultProps}
|
||||
searchTerm={''}
|
||||
customNoOptionsMessage={customMessage}
|
||||
/>,
|
||||
);
|
||||
|
||||
// Set empty channels array to simulate no private channels
|
||||
wrapper.setState({
|
||||
channels: [],
|
||||
loadingChannels: false,
|
||||
});
|
||||
|
||||
// Find the MultiSelect component
|
||||
const multiSelect = wrapper.find('MultiSelect');
|
||||
|
||||
// Should pass the custom message to MultiSelect
|
||||
expect(multiSelect.prop('customNoOptionsMessage')).toEqual(customMessage);
|
||||
});
|
||||
|
||||
test('should not show custom message when user is searching', () => {
|
||||
const customMessage = (
|
||||
<div className='custom-message'>
|
||||
{'No private channels available'}
|
||||
</div>
|
||||
);
|
||||
|
||||
const wrapper = shallowWithIntl(
|
||||
<ChannelSelectorModal
|
||||
{...defaultProps}
|
||||
searchTerm={'test'}
|
||||
customNoOptionsMessage={customMessage}
|
||||
/>,
|
||||
);
|
||||
|
||||
// Set empty channels array
|
||||
wrapper.setState({
|
||||
channels: [],
|
||||
loadingChannels: false,
|
||||
});
|
||||
|
||||
// Find the MultiSelect component
|
||||
const multiSelect = wrapper.find('MultiSelect');
|
||||
|
||||
// Should NOT pass the custom message when searching (let default message show)
|
||||
expect(multiSelect.prop('customNoOptionsMessage')).toBeUndefined();
|
||||
});
|
||||
|
||||
test('should not show custom message when channels are available', () => {
|
||||
const customMessage = (
|
||||
<div className='custom-message'>
|
||||
{'No private channels available'}
|
||||
</div>
|
||||
);
|
||||
|
||||
const wrapper = shallowWithIntl(
|
||||
<ChannelSelectorModal
|
||||
{...defaultProps}
|
||||
searchTerm={''}
|
||||
customNoOptionsMessage={customMessage}
|
||||
/>,
|
||||
);
|
||||
|
||||
// Set channels array with data
|
||||
wrapper.setState({
|
||||
channels: [channel1, channel2],
|
||||
loadingChannels: false,
|
||||
});
|
||||
|
||||
// Find the MultiSelect component
|
||||
const multiSelect = wrapper.find('MultiSelect');
|
||||
|
||||
// Custom message is passed but MultiSelect won't show it because options exist
|
||||
// The important thing is that the component renders normally with channels
|
||||
const options = multiSelect.prop('options') as any[];
|
||||
expect(options.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('excludes group constrained channels when requested', () => {
|
||||
const wrapper = shallowWithIntl(
|
||||
<ChannelSelectorModal
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ type Props = {
|
|||
excludeGroupConstrained?: boolean;
|
||||
excludeTeamIds?: string[];
|
||||
excludeTypes?: string[];
|
||||
customNoOptionsMessage?: React.ReactNode;
|
||||
}
|
||||
|
||||
type State = {
|
||||
|
|
@ -232,6 +233,13 @@ export class ChannelSelectorModal extends React.PureComponent<Props, State> {
|
|||
}
|
||||
const values = this.state.values.map((i): ChannelWithTeamDataValue => ({...i, label: i.display_name, value: i.id}));
|
||||
|
||||
// Only show custom message when there are no options and user hasn't started searching
|
||||
// If user is searching (searchTerm exists), show the default "No results found matching..." message
|
||||
let customNoOptionsMessage;
|
||||
if (this.props.customNoOptionsMessage && !this.props.searchTerm) {
|
||||
customNoOptionsMessage = this.props.customNoOptionsMessage;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
dialogClassName={'a11y__modal more-modal more-direct-channels channel-selector-modal'}
|
||||
|
|
@ -275,6 +283,7 @@ export class ChannelSelectorModal extends React.PureComponent<Props, State> {
|
|||
saving={false}
|
||||
loading={this.state.loadingChannels}
|
||||
placeholderText={defineMessage({id: 'multiselect.addChannelsPlaceholder', defaultMessage: 'Search and add channels'})}
|
||||
customNoOptionsMessage={customNoOptionsMessage}
|
||||
/>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
|
|
|
|||
|
|
@ -562,7 +562,7 @@ export class MultiSelect<T extends Value> extends React.PureComponent<Props<T>,
|
|||
</span>
|
||||
</div>
|
||||
)}
|
||||
{this.props.saveButtonPosition === 'top' &&
|
||||
{this.props.saveButtonPosition === 'top' && (previousButton || nextButton) &&
|
||||
<div className='filter-controls'>
|
||||
{previousButton}
|
||||
{nextButton}
|
||||
|
|
|
|||
|
|
@ -311,6 +311,7 @@
|
|||
"admin.access_control.policy.edit_policy.error.name_required": "Please add a name to the policy",
|
||||
"admin.access_control.policy.edit_policy.error.unassign_channels": "Error unassigning channels: {error}",
|
||||
"admin.access_control.policy.edit_policy.error.update_active_status": "Error updating policy active status: {error}",
|
||||
"admin.access_control.policy.edit_policy.no_private_channels": "There are no private channels available to add to this policy.",
|
||||
"admin.access_control.policy.edit_policy.no_usable_attributes_tooltip": "Please configure user attributes to use the editor.",
|
||||
"admin.access_control.policy.edit_policy.notice.button": "Configure user attributes",
|
||||
"admin.access_control.policy.edit_policy.notice.text": "You havent configured any user attributes yet. Attribute-Based Access Control requires user attributes that are either synced from an external system (like LDAP or SAML) or manually configured and enabled on this server. To start using attribute based access, please configure user attributes in System Attributes.",
|
||||
|
|
|
|||
|
|
@ -325,7 +325,7 @@
|
|||
background: transparent;
|
||||
color: functions.v(center-channel-color);
|
||||
font-size: 22px;
|
||||
line-height: 28px;
|
||||
line-height: 44px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -335,7 +335,7 @@
|
|||
color: functions.v(center-channel-color);
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
line-height: 28px;
|
||||
line-height: 44px;
|
||||
word-break: break-word;
|
||||
}
|
||||
}
|
||||
|
|
@ -1065,8 +1065,8 @@
|
|||
.no-channel-message {
|
||||
width: 100%;
|
||||
margin-top: 40px;
|
||||
color: variables.$gray;
|
||||
font-size: 1.25em;
|
||||
color: (--center-channel-color, 0.72);
|
||||
font-size: 1em;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue