mirror of
https://github.com/mattermost/mattermost.git
synced 2026-05-28 04:35:04 -04:00
Merge 6bbd385e7a into 508f1551e3
This commit is contained in:
commit
efcd305f3b
3 changed files with 183 additions and 35 deletions
|
|
@ -264,7 +264,7 @@ test('MM-T5776 Hide custom profile attributes when visibility is set to hidden @
|
|||
|
||||
/**
|
||||
* Verify that custom profile attributes with visibility set to always
|
||||
* are displayed in the profile popover even if they have no value.
|
||||
* are not displayed in the profile popover if they have no value.
|
||||
*
|
||||
* Precondition:
|
||||
* 1. A test server with valid license to support 'Custom Profile Attributes'
|
||||
|
|
@ -272,7 +272,7 @@ test('MM-T5776 Hide custom profile attributes when visibility is set to hidden @
|
|||
* 3. Other user has values set for custom profile attributes
|
||||
* 4. Two user accounts exist and are members of the same channel
|
||||
*/
|
||||
test('MM-T5777 Always display custom profile attributes with visibility set to always @custom_profile_attributes', async ({
|
||||
test('MM-T5777 Do not display valueless custom profile attributes with visibility set to always @custom_profile_attributes', async ({
|
||||
pw,
|
||||
}) => {
|
||||
// 1. Update the visibility of the Title attribute to always
|
||||
|
|
@ -289,16 +289,9 @@ test('MM-T5777 Always display custom profile attributes with visibility set to a
|
|||
const lastPost = await channelsPage.getLastPost();
|
||||
await channelsPage.openProfilePopover(lastPost);
|
||||
|
||||
// * Verify custom attributes are displayed correctly
|
||||
// * Verify custom attributes without values are not displayed
|
||||
for (const attribute of customAttributes) {
|
||||
if (attribute.name === 'Title') {
|
||||
// * Verify the Title attribute is displayed even though it has no value
|
||||
const popover = channelsPage.userProfilePopover.container;
|
||||
const nameElement = popover.getByText('Title', {exact: false});
|
||||
await expect(nameElement).toBeVisible();
|
||||
} else {
|
||||
await verifyAttributeNotInPopover(channelsPage, attribute.name);
|
||||
}
|
||||
await verifyAttributeNotInPopover(channelsPage, attribute.name);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -279,6 +279,156 @@ describe('components/ProfilePopoverCustomAttributes', () => {
|
|||
expect(screen.queryByText('Text Attribute')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should not render shared-only always-visible attribute labels when the value is missing', () => {
|
||||
const state = {
|
||||
...baseState,
|
||||
entities: {
|
||||
...baseState.entities,
|
||||
users: {
|
||||
profiles: {
|
||||
user_id: TestHelper.getUserMock({
|
||||
id: 'user_id',
|
||||
custom_profile_attributes: {
|
||||
phone_attribute_id: '+1 (555) 123-4567',
|
||||
url_attribute_id: 'https://example.com',
|
||||
select_attribute_id: 'option1',
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
general: {
|
||||
...baseState.entities.general,
|
||||
customProfileAttributes: {
|
||||
...baseState.entities.general.customProfileAttributes,
|
||||
text_attribute_id: {
|
||||
...textAttribute,
|
||||
attrs: {
|
||||
...textAttribute.attrs,
|
||||
visibility: 'always',
|
||||
access_mode: 'shared_only',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
renderWithContext(
|
||||
<Provider store={store}>
|
||||
<ProfilePopoverCustomAttributes {...baseProps}/>
|
||||
</Provider>,
|
||||
);
|
||||
|
||||
expect(screen.queryByText('Text Attribute')).not.toBeInTheDocument();
|
||||
expect(screen.getByText('Phone Number')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should not render always-visible select labels when the value is not displayable', () => {
|
||||
const state = {
|
||||
...baseState,
|
||||
entities: {
|
||||
...baseState.entities,
|
||||
users: {
|
||||
profiles: {
|
||||
user_id: TestHelper.getUserMock({
|
||||
id: 'user_id',
|
||||
custom_profile_attributes: {
|
||||
...userProfile.custom_profile_attributes,
|
||||
select_attribute_id: 'filtered-option',
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
general: {
|
||||
...baseState.entities.general,
|
||||
customProfileAttributes: {
|
||||
...baseState.entities.general.customProfileAttributes,
|
||||
select_attribute_id: {
|
||||
...selectAttribute,
|
||||
attrs: {
|
||||
...selectAttribute.attrs,
|
||||
visibility: 'always',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
renderWithContext(
|
||||
<Provider store={store}>
|
||||
<ProfilePopoverCustomAttributes {...baseProps}/>
|
||||
</Provider>,
|
||||
);
|
||||
|
||||
expect(screen.queryByText('Select Attribute')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('filtered-option')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render multiselect labels only when at least one value is displayable', () => {
|
||||
const hiddenMultiselectAttribute: UserPropertyField = {
|
||||
...selectAttribute,
|
||||
id: 'hidden_multiselect_attribute_id',
|
||||
name: 'Hidden Multiselect Attribute',
|
||||
type: 'multiselect',
|
||||
attrs: {
|
||||
...selectAttribute.attrs,
|
||||
visibility: 'always',
|
||||
},
|
||||
};
|
||||
const visibleMultiselectAttribute: UserPropertyField = {
|
||||
...selectAttribute,
|
||||
id: 'visible_multiselect_attribute_id',
|
||||
name: 'Visible Multiselect Attribute',
|
||||
type: 'multiselect',
|
||||
attrs: {
|
||||
...selectAttribute.attrs,
|
||||
visibility: 'always',
|
||||
},
|
||||
};
|
||||
const state = {
|
||||
...baseState,
|
||||
entities: {
|
||||
...baseState.entities,
|
||||
users: {
|
||||
profiles: {
|
||||
user_id: TestHelper.getUserMock({
|
||||
id: 'user_id',
|
||||
custom_profile_attributes: {
|
||||
hidden_multiselect_attribute_id: ['filtered-option'],
|
||||
visible_multiselect_attribute_id: ['filtered-option', 'option1'],
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
general: {
|
||||
...baseState.entities.general,
|
||||
customProfileAttributes: {
|
||||
hidden_multiselect_attribute_id: hiddenMultiselectAttribute,
|
||||
visible_multiselect_attribute_id: visibleMultiselectAttribute,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const store = mockStore(state);
|
||||
|
||||
renderWithContext(
|
||||
<Provider store={store}>
|
||||
<ProfilePopoverCustomAttributes {...baseProps}/>
|
||||
</Provider>,
|
||||
);
|
||||
|
||||
expect(screen.queryByText('Hidden Multiselect Attribute')).not.toBeInTheDocument();
|
||||
expect(screen.getByText('Visible Multiselect Attribute')).toBeInTheDocument();
|
||||
expect(screen.getByText('Option 1')).toBeInTheDocument();
|
||||
expect(screen.queryByText('filtered-option')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render display_name as the visible label when set', () => {
|
||||
const state = {
|
||||
...baseState,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import React, {useEffect} from 'react';
|
||||
import {useDispatch, useSelector} from 'react-redux';
|
||||
|
||||
import type {UserPropertyValueType} from '@mattermost/types/properties';
|
||||
import type {UserPropertyField, UserPropertyValueType} from '@mattermost/types/properties';
|
||||
|
||||
import {getCustomProfileAttributeValues} from 'mattermost-redux/actions/users';
|
||||
import {getCustomProfileAttributes} from 'mattermost-redux/selectors/entities/general';
|
||||
|
|
@ -23,6 +23,33 @@ type Props = {
|
|||
userID: string;
|
||||
hideStatus?: boolean;
|
||||
}
|
||||
|
||||
const hasDisplayableAttributeValue = (attribute: UserPropertyField, customProfileAttributes: Record<string, string | string[]>): boolean => {
|
||||
const attributeValue = customProfileAttributes[attribute.id];
|
||||
if (Array.isArray(attributeValue)) {
|
||||
if (attributeValue.length === 0) {
|
||||
return false;
|
||||
}
|
||||
} else if (!attributeValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (attribute.type === 'multiselect' || attribute.type === 'select') {
|
||||
const options = attribute.attrs?.options;
|
||||
if (!options?.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Array.isArray(attributeValue)) {
|
||||
return attributeValue.some((value) => options.some((option) => option.id === value));
|
||||
}
|
||||
|
||||
return options.some((option) => option.id === attributeValue);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const ProfilePopoverCustomAttributes = ({
|
||||
userID,
|
||||
hideStatus = false,
|
||||
|
|
@ -49,30 +76,8 @@ const ProfilePopoverCustomAttributes = ({
|
|||
return null;
|
||||
}
|
||||
|
||||
// Check if the attribute has a value
|
||||
const hasValue = userProfile.custom_profile_attributes[attribute.id]?.length > 0;
|
||||
|
||||
if (!hasValue && visibility === 'when_set') {
|
||||
if (!hasDisplayableAttributeValue(attribute, userProfile.custom_profile_attributes)) {
|
||||
return null;
|
||||
} else if (visibility === 'when_set' && (attribute.type === 'multiselect' || attribute.type === 'select')) {
|
||||
const attributeValue = userProfile.custom_profile_attributes[attribute.id];
|
||||
|
||||
// make sure attribute contains legitimate values
|
||||
if (Array.isArray(attributeValue)) {
|
||||
// Handle multiselect
|
||||
const options = attributeValue.map((value) => {
|
||||
return attribute.attrs.options?.find((o) => o.id === value);
|
||||
}).filter((o) => o != null);
|
||||
if (options.length === 0) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// Handle single select
|
||||
const option = attribute.attrs.options?.find((o) => o.id === attributeValue);
|
||||
if (option === undefined) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const valueType = (attribute.attrs?.value_type as UserPropertyValueType) || '';
|
||||
|
|
|
|||
Loading…
Reference in a new issue