diff --git a/client/src/entrypoints/admin/core.js b/client/src/entrypoints/admin/core.js index a39e504a3d..cb633b598d 100644 --- a/client/src/entrypoints/admin/core.js +++ b/client/src/entrypoints/admin/core.js @@ -1,6 +1,7 @@ import $ from 'jquery'; import { escapeHtml } from '../../utils/text'; import { initButtonSelects } from '../../includes/initButtonSelects'; +import { initTagField } from '../../includes/initTagField'; import { initTooltips } from '../../includes/initTooltips'; /* generic function for adding a message to message area through JS alone */ @@ -19,24 +20,6 @@ window.addMessage = addMessage; window.escapeHtml = escapeHtml; -function initTagField(id, autocompleteUrl, options) { - const finalOptions = { - autocomplete: { source: autocompleteUrl }, - preprocessTag(val) { - // Double quote a tag if it contains a space - // and if it isn't already quoted. - if (val && val[0] !== '"' && val.indexOf(' ') > -1) { - return '"' + val + '"'; - } - - return val; - }, - ...options, - }; - - $('#' + id).tagit(finalOptions); -} - window.initTagField = initTagField; /* diff --git a/client/src/includes/initTagField.test.js b/client/src/includes/initTagField.test.js new file mode 100644 index 0000000000..72077e8d97 --- /dev/null +++ b/client/src/includes/initTagField.test.js @@ -0,0 +1,62 @@ +import $ from 'jquery'; + +window.$ = $; + +import { initTagField } from './initTagField'; + +describe('initTagField', () => { + let element; + + const tagitMock = jest.fn(function tagitMockInner() { + element = this; + }); + + window.$.fn.tagit = tagitMock; + + beforeEach(() => { + element = null; + jest.clearAllMocks(); + }); + + it('should not call jQuery tagit if the element is not found', () => { + expect(tagitMock).not.toHaveBeenCalled(); + + initTagField('not-present'); + + expect(tagitMock).not.toHaveBeenCalled(); + }); + + it('should call jQuery tagit if the element is found', () => { + expect(tagitMock).not.toHaveBeenCalled(); + + document.body.innerHTML = ` +
+ +
+ `; + + initTagField('tag-input', '/path/to/autocomplete/', { + someOther: 'option', + }); + + // check the jQuery instance is the correct element + expect(element).toContain(document.getElementById('tag-input')); + + // check the tagit util was called correctly with supplied params + expect(tagitMock).toHaveBeenCalledWith( + expect.objectContaining({ + autocomplete: { source: '/path/to/autocomplete/' }, + someOther: 'option', + }), + ); + + // check the supplied preprocessTag function + const [{ preprocessTag }] = tagitMock.mock.calls[0]; + + expect(preprocessTag).toBeInstanceOf(Function); + + expect(preprocessTag()).toEqual(); + expect(preprocessTag('"flat white"')).toEqual(`"flat white"`); + expect(preprocessTag("'long black'")).toEqual(`"'long black'"`); + }); +}); diff --git a/client/src/includes/initTagField.ts b/client/src/includes/initTagField.ts new file mode 100644 index 0000000000..c714b42432 --- /dev/null +++ b/client/src/includes/initTagField.ts @@ -0,0 +1,42 @@ +import $ from 'jquery'; + +declare global { + interface JQuery { + tagit(...args): void; + } +} + +/** + * Initialises the tag fields using the jQuery tagit widget + * + * @param id - element id to initialise against + * @param source - auto complete URL source + * @param options - Other options passed to jQuery tagit + */ +const initTagField = ( + id: string, + source: string, + options: Record, +): void => { + const tagFieldElement = document.getElementById(id); + + if (!tagFieldElement) return; + + const finalOptions = { + autocomplete: { source }, + preprocessTag(val: any) { + // Double quote a tag if it contains a space + // and if it isn't already quoted. + if (val && val[0] !== '"' && val.indexOf(' ') > -1) { + return '"' + val + '"'; + } + + return val; + }, + ...options, + }; + + $('#' + id).tagit(finalOptions); +}; + +export { initTagField };