Skip to content
Snippets Groups Projects
Commit dd91029b authored by Winnie Hellmann's avatar Winnie Hellmann
Browse files

Style people in issuable search bar (!11402)

Former-commit-id: 068a8d69
parent 2a61dd61
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -100,10 +100,13 @@ class DropdownUtils {
if (token.classList.contains('js-visual-token')) {
const name = token.querySelector('.name');
const value = token.querySelector('.value');
const valueContainer = token.querySelector('.value-container');
const symbol = value && value.dataset.symbol ? value.dataset.symbol : '';
let valueText = '';
 
if (value && value.innerText) {
if (valueContainer && valueContainer.dataset.originalValue) {
valueText = valueContainer.dataset.originalValue;
} else if (value && value.innerText) {
valueText = value.innerText;
}
 
Loading
Loading
import AjaxCache from '~/lib/utils/ajax_cache';
import '~/flash'; /* global Flash */
import FilteredSearchContainer from './container';
import UsersCache from '../lib/utils/users_cache';
 
class FilteredSearchVisualTokens {
static getLastVisualTokenBeforeInput() {
Loading
Loading
@@ -75,12 +76,40 @@ class FilteredSearchVisualTokens {
.catch(() => new Flash('An error occurred while fetching label colors.'));
}
 
static updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) {
const username = tokenValue.replace(/^@/, '');
return UsersCache.retrieve(username)
.then((user) => {
if (!user) {
return;
}
/* eslint-disable no-param-reassign */
tokenValueContainer.dataset.originalValue = tokenValue;
tokenValueElement.innerHTML = `
<img class="avatar s20" src="${user.avatar_url}" alt="${user.name}'s avatar">
${user.name}
`;
/* eslint-enable no-param-reassign */
})
.catch((error) => {
new Flash('An error occurred while fetching user information.'); // eslint-disable-line no-new
throw error;
});
}
static renderVisualTokenValue(parentElement, tokenName, tokenValue) {
const tokenValueContainer = parentElement.querySelector('.value-container');
tokenValueContainer.querySelector('.value').innerText = tokenValue;
const tokenValueElement = tokenValueContainer.querySelector('.value');
tokenValueElement.innerText = tokenValue;
 
if (tokenName.toLowerCase() === 'label') {
const tokenType = tokenName.toLowerCase();
if (tokenType === 'label') {
FilteredSearchVisualTokens.updateLabelTokenColor(tokenValueContainer, tokenValue);
} else if ((tokenType === 'author') || (tokenType === 'assignee')) {
FilteredSearchVisualTokens.updateUserTokenAppearance(
tokenValueContainer, tokenValueElement, tokenValue,
);
}
}
 
Loading
Loading
@@ -146,6 +175,12 @@ class FilteredSearchVisualTokens {
 
if (!lastVisualToken) return '';
 
const valueContainer = lastVisualToken.querySelector('.value-container');
const originalValue = valueContainer && valueContainer.dataset.originalValue;
if (originalValue) {
return originalValue;
}
const value = lastVisualToken.querySelector('.value');
const name = lastVisualToken.querySelector('.name');
 
Loading
Loading
Loading
Loading
@@ -305,4 +305,8 @@ describe('Dropdown Utils', () => {
});
});
});
describe('getSearchQuery', () => {
it('TODO', () => fail());
});
});
import AjaxCache from '~/lib/utils/ajax_cache';
import UsersCache from '~/lib/utils/users_cache';
 
require('~/filtered_search/filtered_search_visual_tokens');
const FilteredSearchSpecHelper = require('../helpers/filtered_search_spec_helper');
Loading
Loading
@@ -406,6 +407,22 @@ describe('Filtered Search Visual Tokens', () => {
expect(subject.getLastTokenPartial()).toEqual(value);
});
 
it('should get last token original value if available', () => {
const originalValue = '@user';
const valueContainer = authorToken.querySelector('.value-container');
valueContainer.dataset.originalValue = originalValue;
const avatar = document.createElement('img');
const valueElement = valueContainer.querySelector('.value');
valueElement.insertAdjacentElement('afterbegin', avatar);
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
authorToken.outerHTML,
);
const lastTokenValue = subject.getLastTokenPartial();
expect(lastTokenValue).toEqual(originalValue);
});
it('should get last token name if there is no value', () => {
const name = 'assignee';
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
Loading
Loading
@@ -633,6 +650,7 @@ describe('Filtered Search Visual Tokens', () => {
const milestoneToken = FilteredSearchSpecHelper.createFilterVisualToken('milestone', 'upcoming');
 
let updateLabelTokenColorSpy;
let updateUserTokenAppearanceSpy;
 
beforeEach(() => {
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(`
Loading
Loading
@@ -644,6 +662,24 @@ describe('Filtered Search Visual Tokens', () => {
 
spyOn(subject, 'updateLabelTokenColor');
updateLabelTokenColorSpy = subject.updateLabelTokenColor;
spyOn(subject, 'updateUserTokenAppearance');
updateUserTokenAppearanceSpy = subject.updateUserTokenAppearance;
});
it('renders a author token value element', () => {
const { tokenNameElement, tokenValueContainer, tokenValueElement } =
findElements(authorToken);
const tokenName = tokenNameElement.innerText;
const tokenValue = 'new value';
subject.renderVisualTokenValue(authorToken, tokenName, tokenValue);
expect(tokenValueElement.innerText).toBe(tokenValue);
expect(updateUserTokenAppearanceSpy.calls.count()).toBe(1);
const expectedArgs = [tokenValueContainer, tokenValueElement, tokenValue];
expect(updateUserTokenAppearanceSpy.calls.argsFor(0)).toEqual(expectedArgs);
expect(updateLabelTokenColorSpy.calls.count()).toBe(0);
});
 
it('renders a label token value element', () => {
Loading
Loading
@@ -658,6 +694,7 @@ describe('Filtered Search Visual Tokens', () => {
expect(updateLabelTokenColorSpy.calls.count()).toBe(1);
const expectedArgs = [tokenValueContainer, tokenValue];
expect(updateLabelTokenColorSpy.calls.argsFor(0)).toEqual(expectedArgs);
expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0);
});
 
it('renders a milestone token value element', () => {
Loading
Loading
@@ -669,6 +706,74 @@ describe('Filtered Search Visual Tokens', () => {
 
expect(tokenValueElement.innerText).toBe(tokenValue);
expect(updateLabelTokenColorSpy.calls.count()).toBe(0);
expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0);
});
});
describe('updateUserTokenAppearance', () => {
let usersCacheSpy;
beforeEach(() => {
spyOn(UsersCache, 'retrieve').and.callFake(username => usersCacheSpy(username));
});
it('displays error if UsersCache throws', (done) => {
spyOn(window, 'Flash');
const dummyError = new Error('Earth rotated backwards');
const { tokenValueContainer, tokenValueElement } = findElements(authorToken);
const tokenValue = tokenValueElement.innerText;
usersCacheSpy = (username) => {
expect(`@${username}`).toBe(tokenValue);
return Promise.reject(dummyError);
};
subject.updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue)
.then(done.fail)
.catch((error) => {
expect(error).toBe(dummyError);
expect(window.Flash.calls.count()).toBe(1);
})
.then(done)
.catch(done.fail);
});
it('does nothing if user cannot be found', (done) => {
const { tokenValueContainer, tokenValueElement } = findElements(authorToken);
const tokenValue = tokenValueElement.innerText;
usersCacheSpy = (username) => {
expect(`@${username}`).toBe(tokenValue);
return Promise.resolve(undefined);
};
subject.updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue)
.then(() => {
expect(tokenValueElement.innerText).toBe(tokenValue);
})
.then(done)
.catch(done.fail);
});
it('replaces author token with avatar and display name', (done) => {
const dummyUser = {
name: 'Important Person',
avatar_url: 'https://host.invalid/mypics/avatar.png',
};
const { tokenValueContainer, tokenValueElement } = findElements(authorToken);
const tokenValue = tokenValueElement.innerText;
usersCacheSpy = (username) => {
expect(`@${username}`).toBe(tokenValue);
return Promise.resolve(dummyUser);
};
subject.updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue)
.then(() => {
expect(tokenValueContainer.dataset.originalValue).toBe(tokenValue);
expect(tokenValueElement.innerText.trim()).toBe(dummyUser.name);
const avatar = tokenValueElement.querySelector('img.avatar');
expect(avatar.src).toBe(dummyUser.avatar_url);
})
.then(done)
.catch(done.fail);
});
});
 
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment