Commit 9d0f8bac authored by Paul Slaughter's avatar Paul Slaughter

Merge branch 'mg-refactor-select2-ajax' into 'master'

Replace ajax methods in select2 instances

See merge request gitlab-org/gitlab!55996
parents c0075c3c 549fb72e
...@@ -2,30 +2,8 @@ import $ from 'jquery'; ...@@ -2,30 +2,8 @@ import $ from 'jquery';
import { escape } from 'lodash'; import { escape } from 'lodash';
import { __ } from '~/locale'; import { __ } from '~/locale';
import Api from './api'; import Api from './api';
import axios from './lib/utils/axios_utils';
import { normalizeHeaders } from './lib/utils/common_utils';
import { loadCSSFile } from './lib/utils/css_utils'; import { loadCSSFile } from './lib/utils/css_utils';
import { select2AxiosTransport } from './lib/utils/select2_utils';
const fetchGroups = (params) => {
axios[params.type.toLowerCase()](params.url, {
params: params.data,
})
.then((res) => {
const results = res.data || [];
const headers = normalizeHeaders(res.headers);
const currentPage = parseInt(headers['X-PAGE'], 10) || 0;
const totalPages = parseInt(headers['X-TOTAL-PAGES'], 10) || 0;
const more = currentPage < totalPages;
params.success({
results,
pagination: {
more,
},
});
})
.catch(params.error);
};
const groupsSelect = () => { const groupsSelect = () => {
loadCSSFile(gon.select2_css_path) loadCSSFile(gon.select2_css_path)
...@@ -51,9 +29,7 @@ const groupsSelect = () => { ...@@ -51,9 +29,7 @@ const groupsSelect = () => {
url: Api.buildUrl(groupsPath), url: Api.buildUrl(groupsPath),
dataType: 'json', dataType: 'json',
quietMillis: 250, quietMillis: 250,
transport(params) { transport: select2AxiosTransport,
fetchGroups(params);
},
data(search, page) { data(search, page) {
return { return {
search, search,
...@@ -63,8 +39,6 @@ const groupsSelect = () => { ...@@ -63,8 +39,6 @@ const groupsSelect = () => {
}; };
}, },
results(data, page) { results(data, page) {
if (data.length) return { results: [] };
const groups = data.length ? data : data.results || []; const groups = data.length ? data : data.results || [];
const more = data.pagination ? data.pagination.more : false; const more = data.pagination ? data.pagination.more : false;
const results = groups.filter((group) => skipGroups.indexOf(group.id) === -1); const results = groups.filter((group) => skipGroups.indexOf(group.id) === -1);
......
...@@ -5,6 +5,7 @@ import Autosave from './autosave'; ...@@ -5,6 +5,7 @@ import Autosave from './autosave';
import AutoWidthDropdownSelect from './issuable/auto_width_dropdown_select'; import AutoWidthDropdownSelect from './issuable/auto_width_dropdown_select';
import { loadCSSFile } from './lib/utils/css_utils'; import { loadCSSFile } from './lib/utils/css_utils';
import { parsePikadayDate, pikadayToString } from './lib/utils/datetime_utility'; import { parsePikadayDate, pikadayToString } from './lib/utils/datetime_utility';
import { select2AxiosTransport } from './lib/utils/select2_utils';
import { queryToObject, objectToQuery } from './lib/utils/url_utility'; import { queryToObject, objectToQuery } from './lib/utils/url_utility';
import UsersSelect from './users_select'; import UsersSelect from './users_select';
import ZenMode from './zen_mode'; import ZenMode from './zen_mode';
...@@ -199,15 +200,16 @@ export default class IssuableForm { ...@@ -199,15 +200,16 @@ export default class IssuableForm {
search, search,
}; };
}, },
results(data) { results({ results }) {
return { return {
// `data` keys are translated so we can't just access them with a string based key // `data` keys are translated so we can't just access them with a string based key
results: data[Object.keys(data)[0]].map((name) => ({ results: results[Object.keys(results)[0]].map((name) => ({
id: name, id: name,
text: name, text: name,
})), })),
}; };
}, },
transport: select2AxiosTransport,
}, },
initSelection(el, callback) { initSelection(el, callback) {
const val = el.val(); const val = el.val();
......
import axios from './axios_utils';
import { normalizeHeaders, parseIntPagination } from './common_utils';
// This is used in the select2 config to replace jQuery.ajax with axios
export const select2AxiosTransport = (params) => {
axios({
method: params.type?.toLowerCase() || 'get',
url: params.url,
params: params.data,
})
.then((res) => {
const results = res.data || [];
const headers = normalizeHeaders(res.headers);
const pagination = parseIntPagination(headers);
const more = pagination.nextPage > pagination.page;
params.success({
results,
pagination: {
more,
},
});
})
.catch(params.error);
};
...@@ -3,6 +3,7 @@ import $ from 'jquery'; ...@@ -3,6 +3,7 @@ import $ from 'jquery';
import Api from '~/api'; import Api from '~/api';
import groupsSelect from '~/groups_select'; import groupsSelect from '~/groups_select';
import { loadCSSFile } from '~/lib/utils/css_utils'; import { loadCSSFile } from '~/lib/utils/css_utils';
import { select2AxiosTransport } from '~/lib/utils/select2_utils';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
const onLimitCheckboxChange = (checked, $limitByNamespaces, $limitByProjects) => { const onLimitCheckboxChange = (checked, $limitByNamespaces, $limitByProjects) => {
...@@ -29,12 +30,13 @@ const getDropdownConfig = (placeholder, apiPath, textProp) => ({ ...@@ -29,12 +30,13 @@ const getDropdownConfig = (placeholder, apiPath, textProp) => ({
}, },
results(data) { results(data) {
return { return {
results: data.map((entity) => ({ results: data.results.map((entity) => ({
id: entity.id, id: entity.id,
text: entity[textProp], text: entity[textProp],
})), })),
}; };
}, },
transport: select2AxiosTransport,
}, },
}); });
......
import $ from 'jquery'; import $ from 'jquery';
import { loadCSSFile } from '~/lib/utils/css_utils'; import { loadCSSFile } from '~/lib/utils/css_utils';
import { select2AxiosTransport } from '~/lib/utils/select2_utils';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import PersistentUserCallout from '~/persistent_user_callout'; import PersistentUserCallout from '~/persistent_user_callout';
...@@ -27,12 +28,13 @@ const getDropdownConfig = (placeholder, url) => ({ ...@@ -27,12 +28,13 @@ const getDropdownConfig = (placeholder, url) => ({
}, },
results(data) { results(data) {
return { return {
results: data.map((entity) => ({ results: data.results.map((entity) => ({
id: entity.source_id, id: entity.source_id,
text: entity.path, text: entity.path,
})), })),
}; };
}, },
transport: select2AxiosTransport,
}, },
}); });
......
import MockAdapter from 'axios-mock-adapter';
import $ from 'jquery';
import { setHTMLFixture } from 'helpers/fixtures';
import waitForPromises from 'helpers/wait_for_promises';
import axios from '~/lib/utils/axios_utils';
import { select2AxiosTransport } from '~/lib/utils/select2_utils';
import 'select2/select2';
const TEST_URL = '/test/api/url';
const TEST_SEARCH_DATA = { extraSearch: 'test' };
const TEST_DATA = [{ id: 1 }];
const TEST_SEARCH = 'FOO';
describe('lib/utils/select2_utils', () => {
let mock;
let resultsSpy;
beforeEach(() => {
setHTMLFixture('<div><input id="root" /></div>');
mock = new MockAdapter(axios);
resultsSpy = jest.fn().mockReturnValue({ results: [] });
});
afterEach(() => {
mock.restore();
});
const setupSelect2 = (input) => {
input.select2({
ajax: {
url: TEST_URL,
quietMillis: 250,
transport: select2AxiosTransport,
data(search, page) {
return {
search,
page,
...TEST_SEARCH_DATA,
};
},
results: resultsSpy,
},
});
};
const setupSelect2AndSearch = async () => {
const $input = $('#root');
setupSelect2($input);
$input.select2('search', TEST_SEARCH);
jest.runOnlyPendingTimers();
await waitForPromises();
};
describe('select2AxiosTransport', () => {
it('uses axios to make request', async () => {
// setup mock response
const replySpy = jest.fn();
mock.onGet(TEST_URL).reply((...args) => replySpy(...args));
await setupSelect2AndSearch();
expect(replySpy).toHaveBeenCalledWith(
expect.objectContaining({
url: TEST_URL,
method: 'get',
params: {
page: 1,
search: TEST_SEARCH,
...TEST_SEARCH_DATA,
},
}),
);
});
it.each`
headers | pagination
${{}} | ${{ more: false }}
${{ 'X-PAGE': '1', 'x-next-page': 2 }} | ${{ more: true }}
`(
'passes results and pagination to results callback, with headers=$headers',
async ({ headers, pagination }) => {
mock.onGet(TEST_URL).reply(200, TEST_DATA, headers);
await setupSelect2AndSearch();
expect(resultsSpy).toHaveBeenCalledWith(
{ results: TEST_DATA, pagination },
1,
expect.anything(),
);
},
);
});
});
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