Commit 4241fb40 authored by Zack Cuddy's avatar Zack Cuddy Committed by Natalia Tepluhina

Global Search - Update Apply Button

parent b0267c1c
......@@ -13,7 +13,7 @@ export default {
ConfidentialityFilter,
},
computed: {
...mapState(['query']),
...mapState(['query', 'sidebarDirty']),
showReset() {
return this.query.state || this.query.confidential;
},
......@@ -32,7 +32,7 @@ export default {
<status-filter />
<confidentiality-filter />
<div class="gl-display-flex gl-align-items-center gl-mt-3">
<gl-button category="primary" variant="confirm" size="small" type="submit">
<gl-button category="primary" variant="confirm" type="submit" :disabled="!sidebarDirty">
{{ __('Apply') }}
</gl-button>
<gl-link v-if="showReset" class="gl-ml-auto" @click="resetQuery">{{
......
......@@ -5,7 +5,7 @@ const header = __('Status');
const filters = {
ANY: {
label: __('Any'),
value: 'all',
value: null,
},
OPEN: {
label: __('Open'),
......
......@@ -2,9 +2,9 @@ import Api from '~/api';
import createFlash from '~/flash';
import { visitUrl, setUrlParams } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import { GROUPS_LOCAL_STORAGE_KEY, PROJECTS_LOCAL_STORAGE_KEY } from './constants';
import { GROUPS_LOCAL_STORAGE_KEY, PROJECTS_LOCAL_STORAGE_KEY, SIDEBAR_PARAMS } from './constants';
import * as types from './mutation_types';
import { loadDataFromLS, setFrequentItemToLS, mergeById } from './utils';
import { loadDataFromLS, setFrequentItemToLS, mergeById, isSidebarDirty } from './utils';
export const fetchGroups = ({ commit }, search) => {
commit(types.REQUEST_GROUPS);
......@@ -86,8 +86,12 @@ export const setFrequentProject = ({ state, commit }, item) => {
commit(types.LOAD_FREQUENT_ITEMS, { key: PROJECTS_LOCAL_STORAGE_KEY, data: frequentItems });
};
export const setQuery = ({ commit }, { key, value }) => {
export const setQuery = ({ state, commit }, { key, value }) => {
commit(types.SET_QUERY, { key, value });
if (SIDEBAR_PARAMS.includes(key)) {
commit(types.SET_SIDEBAR_DIRTY, isSidebarDirty(state.query, state.urlQuery));
}
};
export const applyQuery = ({ state }) => {
......
import { stateFilterData } from '~/search/sidebar/constants/state_filter_data';
import { confidentialFilterData } from '~/search/sidebar/constants/confidential_filter_data';
export const MAX_FREQUENT_ITEMS = 5;
export const MAX_FREQUENCY = 5;
......@@ -5,3 +8,5 @@ export const MAX_FREQUENCY = 5;
export const GROUPS_LOCAL_STORAGE_KEY = 'global-search-frequent-groups';
export const PROJECTS_LOCAL_STORAGE_KEY = 'global-search-frequent-projects';
export const SIDEBAR_PARAMS = [stateFilterData.filterParam, confidentialFilterData.filterParam];
......@@ -7,5 +7,6 @@ export const RECEIVE_PROJECTS_SUCCESS = 'RECEIVE_PROJECTS_SUCCESS';
export const RECEIVE_PROJECTS_ERROR = 'RECEIVE_PROJECTS_ERROR';
export const SET_QUERY = 'SET_QUERY';
export const SET_SIDEBAR_DIRTY = 'SET_SIDEBAR_DIRTY';
export const LOAD_FREQUENT_ITEMS = 'LOAD_FREQUENT_ITEMS';
......@@ -26,6 +26,9 @@ export default {
[types.SET_QUERY](state, { key, value }) {
state.query[key] = value;
},
[types.SET_SIDEBAR_DIRTY](state, value) {
state.sidebarDirty = value;
},
[types.LOAD_FREQUENT_ITEMS](state, { key, data }) {
state.frequentItems[key] = data;
},
......
import { cloneDeep } from 'lodash';
import { GROUPS_LOCAL_STORAGE_KEY, PROJECTS_LOCAL_STORAGE_KEY } from './constants';
const createState = ({ query }) => ({
urlQuery: cloneDeep(query),
query,
groups: [],
fetchingGroups: false,
......@@ -10,5 +12,6 @@ const createState = ({ query }) => ({
[GROUPS_LOCAL_STORAGE_KEY]: [],
[PROJECTS_LOCAL_STORAGE_KEY]: [],
},
sidebarDirty: false,
});
export default createState;
import AccessorUtilities from '../../lib/utils/accessor';
import { MAX_FREQUENT_ITEMS, MAX_FREQUENCY } from './constants';
import { MAX_FREQUENT_ITEMS, MAX_FREQUENCY, SIDEBAR_PARAMS } from './constants';
function extractKeys(object, keyList) {
return Object.fromEntries(keyList.map((key) => [key, object[key]]));
......@@ -80,3 +80,13 @@ export const mergeById = (inflatedData, storedData) => {
return { ...stored, ...data };
});
};
export const isSidebarDirty = (currentQuery, urlQuery) => {
return SIDEBAR_PARAMS.some((param) => {
// userAddParam ensures we don't get a false dirty from null !== undefined
const userAddedParam = !urlQuery[param] && currentQuery[param];
const userChangedExistingParam = urlQuery[param] && urlQuery[param] !== currentQuery[param];
return userAddedParam || userChangedExistingParam;
});
};
import { GlButton, GlLink } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
import { MOCK_QUERY } from 'jest/search/mock_data';
import GlobalSearchSidebar from '~/search/sidebar/components/app.vue';
import ConfidentialityFilter from '~/search/sidebar/components/confidentiality_filter.vue';
import StatusFilter from '~/search/sidebar/components/status_filter.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
Vue.use(Vuex);
describe('GlobalSearchSidebar', () => {
let wrapper;
......@@ -27,21 +27,19 @@ describe('GlobalSearchSidebar', () => {
});
wrapper = shallowMount(GlobalSearchSidebar, {
localVue,
store,
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
const findSidebarForm = () => wrapper.find('form');
const findStatusFilter = () => wrapper.find(StatusFilter);
const findConfidentialityFilter = () => wrapper.find(ConfidentialityFilter);
const findApplyButton = () => wrapper.find(GlButton);
const findResetLinkButton = () => wrapper.find(GlLink);
const findStatusFilter = () => wrapper.findComponent(StatusFilter);
const findConfidentialityFilter = () => wrapper.findComponent(ConfidentialityFilter);
const findApplyButton = () => wrapper.findComponent(GlButton);
const findResetLinkButton = () => wrapper.findComponent(GlLink);
describe('template', () => {
beforeEach(() => {
......@@ -61,6 +59,28 @@ describe('GlobalSearchSidebar', () => {
});
});
describe('ApplyButton', () => {
describe('when sidebarDirty is false', () => {
beforeEach(() => {
createComponent({ sidebarDirty: false });
});
it('disables the button', () => {
expect(findApplyButton().attributes('disabled')).toBe('true');
});
});
describe('when sidebarDirty is true', () => {
beforeEach(() => {
createComponent({ sidebarDirty: true });
});
it('enables the button', () => {
expect(findApplyButton().attributes('disabled')).toBe(undefined);
});
});
});
describe('ResetLinkButton', () => {
describe('with no filter selected', () => {
beforeEach(() => {
......
......@@ -5,7 +5,11 @@ import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import * as urlUtils from '~/lib/utils/url_utility';
import * as actions from '~/search/store/actions';
import { GROUPS_LOCAL_STORAGE_KEY, PROJECTS_LOCAL_STORAGE_KEY } from '~/search/store/constants';
import {
GROUPS_LOCAL_STORAGE_KEY,
PROJECTS_LOCAL_STORAGE_KEY,
SIDEBAR_PARAMS,
} from '~/search/store/constants';
import * as types from '~/search/store/mutation_types';
import createState from '~/search/store/state';
import * as storeUtils from '~/search/store/utils';
......@@ -153,15 +157,24 @@ describe('Global Search Store Actions', () => {
});
});
describe('setQuery', () => {
const payload = { key: 'key1', value: 'value1' };
describe.each`
payload | isDirty | isDirtyMutation
${{ key: SIDEBAR_PARAMS[0], value: 'test' }} | ${false} | ${[{ type: types.SET_SIDEBAR_DIRTY, payload: false }]}
${{ key: SIDEBAR_PARAMS[0], value: 'test' }} | ${true} | ${[{ type: types.SET_SIDEBAR_DIRTY, payload: true }]}
${{ key: SIDEBAR_PARAMS[1], value: 'test' }} | ${false} | ${[{ type: types.SET_SIDEBAR_DIRTY, payload: false }]}
${{ key: SIDEBAR_PARAMS[1], value: 'test' }} | ${true} | ${[{ type: types.SET_SIDEBAR_DIRTY, payload: true }]}
${{ key: 'non-sidebar', value: 'test' }} | ${false} | ${[]}
${{ key: 'non-sidebar', value: 'test' }} | ${true} | ${[]}
`('setQuery', ({ payload, isDirty, isDirtyMutation }) => {
describe(`when filter param is ${payload.key} and utils.isSidebarDirty returns ${isDirty}`, () => {
const expectedMutations = [{ type: types.SET_QUERY, payload }].concat(isDirtyMutation);
it('calls the SET_QUERY mutation', () => {
return testAction({
action: actions.setQuery,
payload,
state,
expectedMutations: [{ type: types.SET_QUERY, payload }],
beforeEach(() => {
storeUtils.isSidebarDirty = jest.fn().mockReturnValue(isDirty);
});
it(`should dispatch the correct mutations`, () => {
return testAction({ action: actions.setQuery, payload, state, expectedMutations });
});
});
});
......
......@@ -72,6 +72,16 @@ describe('Global Search Store Mutations', () => {
});
});
describe('SET_SIDEBAR_DIRTY', () => {
const value = true;
it('sets sidebarDirty to the value', () => {
mutations[types.SET_SIDEBAR_DIRTY](state, value);
expect(state.sidebarDirty).toBe(value);
});
});
describe('LOAD_FREQUENT_ITEMS', () => {
it('sets frequentItems[key] to data', () => {
const payload = { key: 'test-key', data: [1, 2, 3] };
......
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import { MAX_FREQUENCY } from '~/search/store/constants';
import { loadDataFromLS, setFrequentItemToLS, mergeById } from '~/search/store/utils';
import { MAX_FREQUENCY, SIDEBAR_PARAMS } from '~/search/store/constants';
import {
loadDataFromLS,
setFrequentItemToLS,
mergeById,
isSidebarDirty,
} from '~/search/store/utils';
import {
MOCK_LS_KEY,
MOCK_GROUPS,
......@@ -216,4 +221,24 @@ describe('Global Search Store Utils', () => {
});
});
});
describe.each`
description | currentQuery | urlQuery | isDirty
${'identical'} | ${{ [SIDEBAR_PARAMS[0]]: 'default', [SIDEBAR_PARAMS[1]]: 'default' }} | ${{ [SIDEBAR_PARAMS[0]]: 'default', [SIDEBAR_PARAMS[1]]: 'default' }} | ${false}
${'different'} | ${{ [SIDEBAR_PARAMS[0]]: 'default', [SIDEBAR_PARAMS[1]]: 'new' }} | ${{ [SIDEBAR_PARAMS[0]]: 'default', [SIDEBAR_PARAMS[1]]: 'default' }} | ${true}
${'null/undefined'} | ${{ [SIDEBAR_PARAMS[0]]: null, [SIDEBAR_PARAMS[1]]: null }} | ${{ [SIDEBAR_PARAMS[0]]: undefined, [SIDEBAR_PARAMS[1]]: undefined }} | ${false}
${'updated/undefined'} | ${{ [SIDEBAR_PARAMS[0]]: 'new', [SIDEBAR_PARAMS[1]]: 'new' }} | ${{ [SIDEBAR_PARAMS[0]]: undefined, [SIDEBAR_PARAMS[1]]: undefined }} | ${true}
`('isSidebarDirty', ({ description, currentQuery, urlQuery, isDirty }) => {
describe(`with ${description} sidebar query data`, () => {
let res;
beforeEach(() => {
res = isSidebarDirty(currentQuery, urlQuery);
});
it(`returns ${isDirty}`, () => {
expect(res).toStrictEqual(isDirty);
});
});
});
});
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