Skip to content

Commit f89cd5e

Browse files
authoredOct 21, 2024··
Improve string source handling - PR#8 from glenn2223
### Added - A string `source` can now contain `{{term}}`, to accommodate paths where `?term=XXX` isn't suitable - This means you could now use `example.com/search/{{term}}/other-stuff` ### Fixed - A string `source` can now contain a query string (`?`) - It now checks the source and adds either `?` or `&`, whichever is appropriate ### Changes - New `initialiseEnvironment` function allows for quicker test initialisation - now more tests are being created
2 parents 3dc1bcd + c31fe72 commit f89cd5e

File tree

7 files changed

+129
-47
lines changed

7 files changed

+129
-47
lines changed
 

‎CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2828

2929
## UNPUBLISHED
3030

31+
### Added
32+
33+
- A string `source` can now contain `{{term}}`, to accommodate paths where `?term=XXX` isn't suitable
34+
- This means you could now use `example.com/search/{{term}}/other-stuff`
35+
3136
### Fixed
3237

3338
- The mouse being over the popup when it's rendered no longer selects that value whilst typing
39+
- A string `source` can now contain a query string (`?`)
40+
- It now checks the source and adds either `?` or `&`, whichever is appropriate
3441

3542
### Changes
3643

‎README.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ new Autocomplete(
3939
{
4040
// Query this source & expect a JSON response
4141
source: './relative-folder/query.html'
42+
// or './relative-folder/{{term}}/query'
4243
}
4344
)
4445
// Don't forget to start it
@@ -107,7 +108,10 @@ The source of the autocompelte data
107108
#### `SourceTypes<T>`
108109

109110
- `string`
110-
a URL we will `GET` with a `term` querystring parameter (expects a JSON response)
111+
a URL that we will `GET`, expecting a JSON response.
112+
**Note:** the search term is added to the URL in one of two ways
113+
- if `{{term}}` is in the URL, this will be replaced, else
114+
- a `term` querystring parameter is appended to the URL
111115
- `Record` set
112116
an object with string keys and values, treated as label, value respectively
113117
- `string[]`

‎src/index.ts

+12-3
Original file line numberDiff line numberDiff line change
@@ -326,9 +326,18 @@ export class Autocomplete<T = { label: string; value: string }> {
326326
typeof this.options.source === 'string'
327327
? await ((
328328
await window.fetch(
329-
this.options.source +
330-
'?term=' +
331-
encodeURIComponent(data.term),
329+
this.options.source.indexOf('{{term}}') > -1
330+
? this.options.source.replace(
331+
'{{term}}',
332+
encodeURIComponent(data.term),
333+
)
334+
: this.options.source +
335+
(this.options.source.indexOf('?') >
336+
-1
337+
? '&'
338+
: '?') +
339+
'term=' +
340+
encodeURIComponent(data.term),
332341
)
333342
).json() as Promise<ListItemType<T>[]>)
334343
: typeof this.options.source === 'function'

‎tests/index.test.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use strict';
2+
13
import { Autocomplete, AutocompleteStatus } from '../src/index';
24

35
describe('Core Tests', () => {

‎tests/initialiseEnvironment.ts

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/* exported initialiseEnvironment */
2+
3+
'use strict';
4+
import { Autocomplete, AutocompleteStatus } from '../src';
5+
import { SourceTypes } from '../src/Types/SourceTypes';
6+
7+
export function initialiseEnvironment(): {
8+
inputEL: HTMLInputElement;
9+
autocomplete: Autocomplete<{ label: string; value: string }>;
10+
};
11+
export function initialiseEnvironment(newSource: string): {
12+
inputEL: HTMLInputElement;
13+
autocomplete: Autocomplete<{ label: string; value: string }>;
14+
};
15+
export function initialiseEnvironment<T = { label: string; value: string }>(
16+
newSource?: SourceTypes<T>,
17+
): { inputEL: HTMLInputElement; autocomplete: Autocomplete<T> } {
18+
let inputEL = document.createElement('input');
19+
20+
inputEL.classList.add('test');
21+
inputEL = document.body.insertAdjacentElement(
22+
'beforeend',
23+
inputEL,
24+
) as HTMLInputElement;
25+
26+
const autocomplete = new Autocomplete<T>('.test', {
27+
source:
28+
newSource ??
29+
([
30+
{ label: 'First label', value: 'First Value' },
31+
{ label: 'Second label', value: 'Second Value' },
32+
{ label: 'Third label', value: 'Third Value' },
33+
{ label: 'Final label', value: 'Final Value' },
34+
] as SourceTypes<T>),
35+
onOpen: (e, data) => {
36+
data.ul.style.width = `${(e.target as HTMLInputElement).width}px`;
37+
},
38+
});
39+
40+
autocomplete.start();
41+
42+
it('Setup complete with "started" state', () =>
43+
expect(autocomplete.status).toBe(AutocompleteStatus.Started));
44+
45+
return { inputEL, autocomplete };
46+
}

‎tests/mouseover.test.ts

+4-43
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,11 @@
1-
import { Autocomplete, AutocompleteStatus } from '../src/index';
1+
'use strict';
2+
3+
import { initialiseEnvironment } from './initialiseEnvironment';
24

35
jest.useFakeTimers();
46

57
describe('Mouseover Tests', () => {
6-
let inputEL: HTMLInputElement, autocomplete: Autocomplete;
7-
8-
describe('Test environment:-', () => {
9-
it('has added element', () => {
10-
inputEL = document.createElement('input');
11-
12-
inputEL.classList.add('test');
13-
inputEL = document.body.insertAdjacentElement(
14-
'beforeend',
15-
inputEL,
16-
) as HTMLInputElement;
17-
18-
expect(inputEL).not.toBeNull();
19-
});
20-
21-
it('has created autocomplete', () => {
22-
autocomplete = new Autocomplete('.test', {
23-
source: [
24-
{ label: 'First label', value: 'First Value' },
25-
{ label: 'Second label', value: 'Second Value' },
26-
{ label: 'Third label', value: 'Third Value' },
27-
{ label: 'Final label', value: 'Final Value' },
28-
],
29-
onOpen: (e, data) => {
30-
data.ul.style.width = `${
31-
(e.target as HTMLInputElement).width
32-
}px`;
33-
},
34-
});
35-
36-
expect(autocomplete).not.toBeNull();
37-
});
38-
39-
it('has initial state of "stopped"', () =>
40-
expect(autocomplete.status).toBe(AutocompleteStatus.Stopped));
41-
42-
it('"start" should not throw', () =>
43-
expect(autocomplete.start).not.toThrow());
44-
45-
it('now has "started" state', () =>
46-
expect(autocomplete.status).toBe(AutocompleteStatus.Started));
47-
});
8+
const { inputEL, autocomplete } = initialiseEnvironment();
489

4910
describe('Mouse over', () => {
5011
beforeEach(() => {

‎tests/term.test.ts

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use strict';
2+
3+
import { initialiseEnvironment } from './initialiseEnvironment';
4+
5+
//@ts-ignore
6+
global.fetch = jest.fn(() =>
7+
Promise.resolve({
8+
json: () => Promise.resolve([{ label: 'Test', value: 'Test' }]),
9+
}),
10+
);
11+
12+
describe('Term Tests', () => {
13+
describe('simple string (no QS):-', () => {
14+
const { inputEL } = initialiseEnvironment('https://example.com');
15+
16+
inputEL.value = 'Test';
17+
18+
inputEL.dispatchEvent(new Event('change'));
19+
20+
it('appends "?term="', () =>
21+
expect(fetch).toHaveBeenCalledWith(
22+
'https://example.com?term=Test',
23+
));
24+
});
25+
26+
describe('string with `?`:-', () => {
27+
const { inputEL } = initialiseEnvironment('https://example.com?test=1');
28+
29+
inputEL.value = 'Test';
30+
31+
inputEL.dispatchEvent(new Event('change'));
32+
33+
it('appends "&term="', () =>
34+
expect(fetch).toHaveBeenCalledWith(
35+
'https://example.com?test=1&term=Test',
36+
));
37+
});
38+
39+
describe('string with `{{term}}`:-', () => {
40+
const { inputEL } = initialiseEnvironment(
41+
'https://example.com/search/{{term}}/suffix',
42+
);
43+
44+
inputEL.value = 'Test';
45+
46+
inputEL.dispatchEvent(new Event('change'));
47+
48+
it('embeds the "term"', () =>
49+
expect(fetch).toHaveBeenCalledWith(
50+
'https://example.com/search/Test/suffix',
51+
));
52+
});
53+
});

0 commit comments

Comments
 (0)
Please sign in to comment.