Commit 43f2004e authored by Miguel Rincon's avatar Miguel Rincon

Refactor to rename token values to suggestions

This change updates the name "token values" to "suggestions" to make
the gitlab and gitlab-ui terminology consistent.

This way author can more easily identify the features of base_token.vue
as opposed to GlFilteredToken from gitlab-ui.
parent d2580206
......@@ -215,35 +215,35 @@ export function urlQueryToFilter(query = '', options = {}) {
/**
* Returns array of token values from localStorage
* based on provided recentTokenValuesStorageKey
* based on provided recentSuggestionsStorageKey
*
* @param {String} recentTokenValuesStorageKey
* @param {String} recentSuggestionsStorageKey
* @returns
*/
export function getRecentlyUsedTokenValues(recentTokenValuesStorageKey) {
let recentlyUsedTokenValues = [];
export function getRecentlyUsedSuggestions(recentSuggestionsStorageKey) {
let recentlyUsedSuggestions = [];
if (AccessorUtilities.isLocalStorageAccessSafe()) {
recentlyUsedTokenValues = JSON.parse(localStorage.getItem(recentTokenValuesStorageKey)) || [];
recentlyUsedSuggestions = JSON.parse(localStorage.getItem(recentSuggestionsStorageKey)) || [];
}
return recentlyUsedTokenValues;
return recentlyUsedSuggestions;
}
/**
* Sets provided token value to recently used array
* within localStorage for provided recentTokenValuesStorageKey
* within localStorage for provided recentSuggestionsStorageKey
*
* @param {String} recentTokenValuesStorageKey
* @param {String} recentSuggestionsStorageKey
* @param {Object} tokenValue
*/
export function setTokenValueToRecentlyUsed(recentTokenValuesStorageKey, tokenValue) {
const recentlyUsedTokenValues = getRecentlyUsedTokenValues(recentTokenValuesStorageKey);
export function setTokenValueToRecentlyUsed(recentSuggestionsStorageKey, tokenValue) {
const recentlyUsedSuggestions = getRecentlyUsedSuggestions(recentSuggestionsStorageKey);
recentlyUsedTokenValues.splice(0, 0, { ...tokenValue });
recentlyUsedSuggestions.splice(0, 0, { ...tokenValue });
if (AccessorUtilities.isLocalStorageAccessSafe()) {
localStorage.setItem(
recentTokenValuesStorageKey,
JSON.stringify(uniqWith(recentlyUsedTokenValues, isEqual).slice(0, MAX_RECENT_TOKENS_SIZE)),
recentSuggestionsStorageKey,
JSON.stringify(uniqWith(recentlyUsedSuggestions, isEqual).slice(0, MAX_RECENT_TOKENS_SIZE)),
);
}
}
......@@ -74,13 +74,13 @@ export default {
:config="config"
:value="value"
:active="active"
:tokens-list-loading="loading"
:token-values="authors"
:suggestions-loading="loading"
:suggestions="authors"
:fn-active-token-value="getActiveAuthor"
:default-token-values="defaultAuthors"
:preloaded-token-values="preloadedAuthors"
:recent-token-values-storage-key="config.recentTokenValuesStorageKey"
@fetch-token-values="fetchAuthorBySearchTerm"
:default-suggestions="defaultAuthors"
:preloaded-suggestions="preloadedAuthors"
:recent-suggestions-storage-key="config.recentSuggestionsStorageKey"
@fetch-suggestions="fetchAuthorBySearchTerm"
v-on="$listeners"
>
<template #view="{ viewTokenProps: { inputValue, activeTokenValue } }">
......@@ -93,9 +93,9 @@ export default {
/>
<span>{{ activeTokenValue ? activeTokenValue.name : inputValue }}</span>
</template>
<template #token-values-list="{ tokenValues }">
<template #suggestions-list="{ suggestions }">
<gl-filtered-search-suggestion
v-for="author in tokenValues"
v-for="author in suggestions"
:key="author.username"
:value="author.username"
>
......
......@@ -8,7 +8,7 @@ import {
} from '@gitlab/ui';
import { DEBOUNCE_DELAY } from '../constants';
import { getRecentlyUsedTokenValues, setTokenValueToRecentlyUsed } from '../filtered_search_utils';
import { getRecentlyUsedSuggestions, setTokenValueToRecentlyUsed } from '../filtered_search_utils';
export default {
components: {
......@@ -31,12 +31,12 @@ export default {
type: Boolean,
required: true,
},
tokensListLoading: {
suggestionsLoading: {
type: Boolean,
required: false,
default: false,
},
tokenValues: {
suggestions: {
type: Array,
required: false,
default: () => [],
......@@ -44,21 +44,21 @@ export default {
fnActiveTokenValue: {
type: Function,
required: false,
default: (tokenValues, currentTokenValue) => {
return tokenValues.find(({ value }) => value === currentTokenValue);
default: (suggestions, currentTokenValue) => {
return suggestions.find(({ value }) => value === currentTokenValue);
},
},
defaultTokenValues: {
defaultSuggestions: {
type: Array,
required: false,
default: () => [],
},
preloadedTokenValues: {
preloadedSuggestions: {
type: Array,
required: false,
default: () => [],
},
recentTokenValuesStorageKey: {
recentSuggestionsStorageKey: {
type: String,
required: false,
default: '',
......@@ -77,21 +77,21 @@ export default {
data() {
return {
searchKey: '',
recentTokenValues: this.recentTokenValuesStorageKey
? getRecentlyUsedTokenValues(this.recentTokenValuesStorageKey)
recentSuggestions: this.recentSuggestionsStorageKey
? getRecentlyUsedSuggestions(this.recentSuggestionsStorageKey)
: [],
loading: false,
};
},
computed: {
isRecentTokenValuesEnabled() {
return Boolean(this.recentTokenValuesStorageKey);
isRecentSuggestionsEnabled() {
return Boolean(this.recentSuggestionsStorageKey);
},
recentTokenIds() {
return this.recentTokenValues.map((tokenValue) => tokenValue[this.valueIdentifier]);
return this.recentSuggestions.map((tokenValue) => tokenValue[this.valueIdentifier]);
},
preloadedTokenIds() {
return this.preloadedTokenValues.map((tokenValue) => tokenValue[this.valueIdentifier]);
return this.preloadedSuggestions.map((tokenValue) => tokenValue[this.valueIdentifier]);
},
currentTokenValue() {
if (this.fnCurrentTokenValue) {
......@@ -100,17 +100,17 @@ export default {
return this.value.data.toLowerCase();
},
activeTokenValue() {
return this.fnActiveTokenValue(this.tokenValues, this.currentTokenValue);
return this.fnActiveTokenValue(this.suggestions, this.currentTokenValue);
},
/**
* Return all the tokenValues when searchKey is present
* otherwise return only the tokenValues which aren't
* Return all the suggestions when searchKey is present
* otherwise return only the suggestions which aren't
* present in "Recently used"
*/
availableTokenValues() {
availableSuggestions() {
return this.searchKey
? this.tokenValues
: this.tokenValues.filter(
? this.suggestions
: this.suggestions.filter(
(tokenValue) =>
!this.recentTokenIds.includes(tokenValue[this.valueIdentifier]) &&
!this.preloadedTokenIds.includes(tokenValue[this.valueIdentifier]),
......@@ -121,8 +121,8 @@ export default {
active: {
immediate: true,
handler(newValue) {
if (!newValue && !this.tokenValues.length) {
this.$emit('fetch-token-values', this.value.data);
if (!newValue && !this.suggestions.length) {
this.$emit('fetch-suggestions', this.value.data);
}
},
},
......@@ -131,7 +131,7 @@ export default {
handleInput({ data }) {
this.searchKey = data;
setTimeout(() => {
if (!this.tokensListLoading) this.$emit('fetch-token-values', data);
if (!this.suggestionsLoading) this.$emit('fetch-suggestions', data);
}, DEBOUNCE_DELAY);
},
handleTokenValueSelected(activeTokenValue) {
......@@ -140,11 +140,11 @@ export default {
// 2. User has actually selected a value
// 3. Selected value is not part of preloaded list.
if (
this.isRecentTokenValuesEnabled &&
this.isRecentSuggestionsEnabled &&
activeTokenValue &&
!this.preloadedTokenIds.includes(activeTokenValue[this.valueIdentifier])
) {
setTokenValueToRecentlyUsed(this.recentTokenValuesStorageKey, activeTokenValue);
setTokenValueToRecentlyUsed(this.recentSuggestionsStorageKey, activeTokenValue);
}
},
},
......@@ -168,9 +168,9 @@ export default {
<slot name="view" :view-token-props="{ ...viewTokenProps, activeTokenValue }"></slot>
</template>
<template #suggestions>
<template v-if="defaultTokenValues.length">
<template v-if="defaultSuggestions.length">
<gl-filtered-search-suggestion
v-for="token in defaultTokenValues"
v-for="token in defaultSuggestions"
:key="token.value"
:value="token.value"
>
......@@ -178,19 +178,19 @@ export default {
</gl-filtered-search-suggestion>
<gl-dropdown-divider />
</template>
<template v-if="isRecentTokenValuesEnabled && recentTokenValues.length && !searchKey">
<template v-if="isRecentSuggestionsEnabled && recentSuggestions.length && !searchKey">
<gl-dropdown-section-header>{{ __('Recently used') }}</gl-dropdown-section-header>
<slot name="token-values-list" :token-values="recentTokenValues"></slot>
<slot name="suggestions-list" :suggestions="recentSuggestions"></slot>
<gl-dropdown-divider />
</template>
<slot
v-if="preloadedTokenValues.length && !searchKey"
name="token-values-list"
:token-values="preloadedTokenValues"
v-if="preloadedSuggestions.length && !searchKey"
name="suggestions-list"
:suggestions="preloadedSuggestions"
></slot>
<gl-loading-icon v-if="tokensListLoading" />
<gl-loading-icon v-if="suggestionsLoading" />
<template v-else>
<slot name="token-values-list" :token-values="availableTokenValues"></slot>
<slot name="suggestions-list" :suggestions="availableSuggestions"></slot>
</template>
</template>
</gl-filtered-search-token>
......
......@@ -96,12 +96,12 @@ export default {
:config="config"
:value="value"
:active="active"
:tokens-list-loading="loading"
:token-values="labels"
:suggestions-loading="loading"
:suggestions="labels"
:fn-active-token-value="getActiveLabel"
:default-token-values="defaultLabels"
:recent-token-values-storage-key="config.recentTokenValuesStorageKey"
@fetch-token-values="fetchLabelBySearchTerm"
:default-suggestions="defaultLabels"
:recent-suggestions-storage-key="config.recentSuggestionsStorageKey"
@fetch-suggestions="fetchLabelBySearchTerm"
v-on="$listeners"
>
<template
......@@ -115,9 +115,9 @@ export default {
>~{{ activeTokenValue ? getLabelName(activeTokenValue) : inputValue }}</gl-token
>
</template>
<template #token-values-list="{ tokenValues }">
<template #suggestions-list="{ suggestions }">
<gl-filtered-search-suggestion
v-for="label in tokenValues"
v-for="label in suggestions"
:key="label.id"
:value="getLabelName(label)"
>
......
......@@ -65,7 +65,7 @@ export default {
symbol: '@',
token: AuthorToken,
operators: OPERATOR_IS_ONLY,
recentTokenValuesStorageKey: `${this.groupFullPath}-epics-recent-tokens-author_username`,
recentSuggestionsStorageKey: `${this.groupFullPath}-epics-recent-tokens-author_username`,
fetchAuthors: Api.users.bind(Api),
preloadedAuthors,
},
......@@ -77,7 +77,7 @@ export default {
symbol: '~',
token: LabelToken,
operators: OPERATOR_IS_ONLY,
recentTokenValuesStorageKey: `${this.groupFullPath}-epics-recent-tokens-label_name`,
recentSuggestionsStorageKey: `${this.groupFullPath}-epics-recent-tokens-label_name`,
fetchLabels: (search = '') => {
const params = {
only_group_labels: true,
......
......@@ -775,7 +775,7 @@ export const mockAuthorTokenConfig = {
symbol: '@',
token: AuthorToken,
operators: OPERATOR_IS_ONLY,
recentTokenValuesStorageKey: 'gitlab-org-epics-recent-tokens-author_username',
recentSuggestionsStorageKey: 'gitlab-org-epics-recent-tokens-author_username',
fetchAuthors: expect.any(Function),
preloadedAuthors: [],
};
......@@ -788,7 +788,7 @@ export const mockLabelTokenConfig = {
symbol: '~',
token: LabelToken,
operators: OPERATOR_IS_ONLY,
recentTokenValuesStorageKey: 'gitlab-org-epics-recent-tokens-label_name',
recentSuggestionsStorageKey: 'gitlab-org-epics-recent-tokens-label_name',
fetchLabels: expect.any(Function),
};
......
......@@ -11,7 +11,7 @@ import {
processFilters,
filterToQueryObject,
urlQueryToFilter,
getRecentlyUsedTokenValues,
getRecentlyUsedSuggestions,
setTokenValueToRecentlyUsed,
} from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
......@@ -328,32 +328,32 @@ describe('urlQueryToFilter', () => {
);
});
describe('getRecentlyUsedTokenValues', () => {
describe('getRecentlyUsedSuggestions', () => {
useLocalStorageSpy();
beforeEach(() => {
localStorage.removeItem(mockStorageKey);
});
it('returns array containing recently used token values from provided recentTokenValuesStorageKey', () => {
it('returns array containing recently used token values from provided recentSuggestionsStorageKey', () => {
setLocalStorageAvailability(true);
const mockExpectedArray = [{ foo: 'bar' }];
localStorage.setItem(mockStorageKey, JSON.stringify(mockExpectedArray));
expect(getRecentlyUsedTokenValues(mockStorageKey)).toEqual(mockExpectedArray);
expect(getRecentlyUsedSuggestions(mockStorageKey)).toEqual(mockExpectedArray);
});
it('returns empty array when provided recentTokenValuesStorageKey does not have anything in localStorage', () => {
it('returns empty array when provided recentSuggestionsStorageKey does not have anything in localStorage', () => {
setLocalStorageAvailability(true);
expect(getRecentlyUsedTokenValues(mockStorageKey)).toEqual([]);
expect(getRecentlyUsedSuggestions(mockStorageKey)).toEqual([]);
});
it('returns empty array when when access to localStorage is not available', () => {
setLocalStorageAvailability(false);
expect(getRecentlyUsedTokenValues(mockStorageKey)).toEqual([]);
expect(getRecentlyUsedSuggestions(mockStorageKey)).toEqual([]);
});
});
......@@ -366,7 +366,7 @@ describe('setTokenValueToRecentlyUsed', () => {
localStorage.removeItem(mockStorageKey);
});
it('adds provided tokenValue to localStorage for recentTokenValuesStorageKey', () => {
it('adds provided tokenValue to localStorage for recentSuggestionsStorageKey', () => {
setLocalStorageAvailability(true);
setTokenValueToRecentlyUsed(mockStorageKey, mockTokenValue1);
......
......@@ -94,7 +94,7 @@ describe('AuthorToken', () => {
it('calls `config.fetchAuthors` with provided searchTerm param', () => {
jest.spyOn(wrapper.vm.config, 'fetchAuthors');
getBaseToken().vm.$emit('fetch-token-values', mockAuthors[0].username);
getBaseToken().vm.$emit('fetch-suggestions', mockAuthors[0].username);
expect(wrapper.vm.config.fetchAuthors).toHaveBeenCalledWith(
mockAuthorToken.fetchPath,
......@@ -105,17 +105,17 @@ describe('AuthorToken', () => {
it('sets response to `authors` when request is succesful', () => {
jest.spyOn(wrapper.vm.config, 'fetchAuthors').mockResolvedValue(mockAuthors);
getBaseToken().vm.$emit('fetch-token-values', 'root');
getBaseToken().vm.$emit('fetch-suggestions', 'root');
return waitForPromises().then(() => {
expect(getBaseToken().props('tokenValues')).toEqual(mockAuthors);
expect(getBaseToken().props('suggestions')).toEqual(mockAuthors);
});
});
it('calls `createFlash` with flash error message when request fails', () => {
jest.spyOn(wrapper.vm.config, 'fetchAuthors').mockRejectedValue({});
getBaseToken().vm.$emit('fetch-token-values', 'root');
getBaseToken().vm.$emit('fetch-suggestions', 'root');
return waitForPromises().then(() => {
expect(createFlash).toHaveBeenCalledWith({
......@@ -127,17 +127,17 @@ describe('AuthorToken', () => {
it('sets `loading` to false when request completes', async () => {
jest.spyOn(wrapper.vm.config, 'fetchAuthors').mockRejectedValue({});
getBaseToken().vm.$emit('fetch-token-values', 'root');
getBaseToken().vm.$emit('fetch-suggestions', 'root');
await waitForPromises();
expect(getBaseToken().props('tokensListLoading')).toBe(false);
expect(getBaseToken().props('suggestionsLoading')).toBe(false);
});
});
});
describe('template', () => {
const activateTokenValuesList = async () => {
const activateSuggestionsList = async () => {
const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment);
const suggestionsSegment = tokenSegments.at(2);
suggestionsSegment.vm.$emit('activate');
......@@ -154,7 +154,7 @@ describe('AuthorToken', () => {
expect(baseTokenEl.exists()).toBe(true);
expect(baseTokenEl.props()).toMatchObject({
tokenValues: mockAuthors,
suggestions: mockAuthors,
fnActiveTokenValue: wrapper.vm.getActiveAuthor,
});
});
......@@ -221,7 +221,7 @@ describe('AuthorToken', () => {
stubs: { Portal: true },
});
await activateTokenValuesList();
await activateSuggestionsList();
const suggestions = wrapper.findAll(GlFilteredSearchSuggestion);
......@@ -252,7 +252,7 @@ describe('AuthorToken', () => {
stubs: { Portal: true },
});
await activateTokenValuesList();
await activateSuggestionsList();
const suggestions = wrapper.findAll(GlFilteredSearchSuggestion);
......
......@@ -7,7 +7,7 @@ import {
import { DEFAULT_LABELS } from '~/vue_shared/components/filtered_search_bar/constants';
import {
getRecentlyUsedTokenValues,
getRecentlyUsedSuggestions,
setTokenValueToRecentlyUsed,
} from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
......@@ -49,10 +49,10 @@ const mockProps = {
config: mockLabelToken,
value: { data: '' },
active: false,
tokenValues: [],
tokensListLoading: false,
defaultTokenValues: DEFAULT_LABELS,
recentTokenValuesStorageKey: mockStorageKey,
suggestions: [],
suggestionsLoading: false,
defaultSuggestions: DEFAULT_LABELS,
recentSuggestionsStorageKey: mockStorageKey,
fnCurrentTokenValue: jest.fn(),
};
......@@ -83,7 +83,7 @@ describe('BaseToken', () => {
props: {
...mockProps,
value: { data: `"${mockRegularLabel.title}"` },
tokenValues: mockLabels,
suggestions: mockLabels,
},
});
});
......@@ -93,8 +93,8 @@ describe('BaseToken', () => {
});
describe('data', () => {
it('calls `getRecentlyUsedTokenValues` to populate `recentTokenValues` when `recentTokenValuesStorageKey` is defined', () => {
expect(getRecentlyUsedTokenValues).toHaveBeenCalledWith(mockStorageKey);
it('calls `getRecentlyUsedSuggestions` to populate `recentSuggestions` when `recentSuggestionsStorageKey` is defined', () => {
expect(getRecentlyUsedSuggestions).toHaveBeenCalledWith(mockStorageKey);
});
});
......@@ -147,15 +147,15 @@ describe('BaseToken', () => {
wrapperWithTokenActive.destroy();
});
it('emits `fetch-token-values` event on the component when value of this prop is changed to false and `tokenValues` array is empty', async () => {
it('emits `fetch-suggestions` event on the component when value of this prop is changed to false and `suggestions` array is empty', async () => {
wrapperWithTokenActive.setProps({
active: false,
});
await wrapperWithTokenActive.vm.$nextTick();
expect(wrapperWithTokenActive.emitted('fetch-token-values')).toBeTruthy();
expect(wrapperWithTokenActive.emitted('fetch-token-values')).toEqual([
expect(wrapperWithTokenActive.emitted('fetch-suggestions')).toBeTruthy();
expect(wrapperWithTokenActive.emitted('fetch-suggestions')).toEqual([
[`"${mockRegularLabel.title}"`],
]);
});
......@@ -164,7 +164,7 @@ describe('BaseToken', () => {
describe('methods', () => {
describe('handleTokenValueSelected', () => {
it('calls `setTokenValueToRecentlyUsed` when `recentTokenValuesStorageKey` is defined', () => {
it('calls `setTokenValueToRecentlyUsed` when `recentSuggestionsStorageKey` is defined', () => {
const mockTokenValue = {
id: 1,
title: 'Foo',
......@@ -175,14 +175,14 @@ describe('BaseToken', () => {
expect(setTokenValueToRecentlyUsed).toHaveBeenCalledWith(mockStorageKey, mockTokenValue);
});
it('does not add token from preloadedTokenValues', async () => {
it('does not add token from preloadedSuggestions', async () => {
const mockTokenValue = {
id: 1,
title: 'Foo',
};
wrapper.setProps({
preloadedTokenValues: [mockTokenValue],
preloadedSuggestions: [mockTokenValue],
});
await wrapper.vm.$nextTick();
......@@ -228,7 +228,7 @@ describe('BaseToken', () => {
wrapperWithNoStubs.destroy();
});
it('emits `fetch-token-values` event on component after a delay when component emits `input` event', async () => {
it('emits `fetch-suggestions` event on component after a delay when component emits `input` event', async () => {
jest.useFakeTimers();
wrapperWithNoStubs.find(GlFilteredSearchToken).vm.$emit('input', { data: 'foo' });
......@@ -236,8 +236,8 @@ describe('BaseToken', () => {
jest.runAllTimers();
expect(wrapperWithNoStubs.emitted('fetch-token-values')).toBeTruthy();
expect(wrapperWithNoStubs.emitted('fetch-token-values')[2]).toEqual(['foo']);
expect(wrapperWithNoStubs.emitted('fetch-suggestions')).toBeTruthy();
expect(wrapperWithNoStubs.emitted('fetch-suggestions')[2]).toEqual(['foo']);
});
});
});
......
......@@ -159,7 +159,7 @@ describe('LabelToken', () => {
expect(baseTokenEl.exists()).toBe(true);
expect(baseTokenEl.props()).toMatchObject({
tokenValues: mockLabels,
suggestions: mockLabels,
fnActiveTokenValue: wrapper.vm.getActiveLabel,
});
});
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment