Skip to content

Commit 3b41e4b

Browse files
Throw an error if more than one reference of a suggestion subcomponent is found (#40)
Also, ditch the part of the README that says the suggestions component will clear its markup, because it just wasn't true. Also, it's useful! Also added some missing tests.
1 parent 6ce2f0e commit 3b41e4b

File tree

4 files changed

+107
-21
lines changed

4 files changed

+107
-21
lines changed

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,8 @@ css classes.
9696

9797
Omnibox Suggestions is a container for your lists of suggestions to be rendered in. It has
9898
additional subcomponents that allow you to slot in markup for different parts needed for the
99-
suggestions to render. Please note that this is the only component which **will not respect any of
100-
the markup you provide**. Any markup inside here that is not one of the documented subcomponents
101-
will be removed.
99+
suggestions to render. You can include at most one instance of an individual subcomponent. If you
100+
include more than one, an Error will be thrown.
102101

103102
#### Omnibox Suggestion Item `<ngc-omnibox-suggestion-item>` _(Required)_
104103
The Suggestion Item component gives you a slot for markup for a single suggestion. This markup

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ngc-omnibox",
3-
"version": "0.1.2",
3+
"version": "0.1.3",
44
"description": "A modern, flexible, Angular 1.x autocomplete library with limited assumptions.",
55
"main": "dist/ngc-omnibox.js",
66
"scripts": {

spec/tests/angularComponent/ngcModifySuggestionsTemplateFactorySpec.js

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,54 @@ const categorizedTemplateOutput = [
5454
'</div>'
5555
].join('');
5656

57+
const loadingElTemplate = [
58+
'<ngc-omnibox-suggestion-item>',
59+
'{{suggestion.sample_item_text}}',
60+
'</ngc-omnibox-suggestion-item>',
61+
'<ngc-omnibox-suggestion-loading></ngc-omnibox-suggestion-loading>'
62+
].join('');
63+
64+
const loadingElTemplateOutput = [
65+
'<ngc-omnibox-suggestion-item ng-repeat="suggestion in omnibox.suggestions" ',
66+
'suggestion="suggestion" ',
67+
'ng-attr-aria-selected="{{suggestionItem.isHighlighted() || undefined}}" ',
68+
'ng-attr-aria-readonly="{{suggestionItem.isSelectable() === false || undefined}}" ',
69+
'ng-mouseenter="suggestionItem.handleMouseEnter()" ',
70+
'ng-mouseleave="suggestionItem.handleMouseLeave()" ng-click="suggestionItem.handleClick()">',
71+
'{{suggestion.sample_item_text}}',
72+
'</ngc-omnibox-suggestion-item>',
73+
'<ngc-omnibox-suggestion-loading role="progressbar" ng-if="omnibox.shouldShowLoadingElement">',
74+
'</ngc-omnibox-suggestion-loading>'
75+
].join('');
76+
77+
const noResultsElTemplate = [
78+
'<ngc-omnibox-suggestion-item>',
79+
'{{suggestion.sample_item_text}}',
80+
'</ngc-omnibox-suggestion-item>',
81+
'<ngc-omnibox-suggestion-empty></ngc-omnibox-suggestion-empty>'
82+
].join('');
83+
84+
const noResultsElTemplateOutput = [
85+
'<ngc-omnibox-suggestion-item ng-repeat="suggestion in omnibox.suggestions" ',
86+
'suggestion="suggestion" ',
87+
'ng-attr-aria-selected="{{suggestionItem.isHighlighted() || undefined}}" ',
88+
'ng-attr-aria-readonly="{{suggestionItem.isSelectable() === false || undefined}}" ',
89+
'ng-mouseenter="suggestionItem.handleMouseEnter()" ',
90+
'ng-mouseleave="suggestionItem.handleMouseLeave()" ng-click="suggestionItem.handleClick()">',
91+
'{{suggestion.sample_item_text}}',
92+
'</ngc-omnibox-suggestion-item>',
93+
// vv I think this is a bug in jsdom
94+
'<ngc-omnibox-suggestion-empty ng-if="!omnibox.hasSuggestions &amp;&amp; !omnibox.isLoading">',
95+
'</ngc-omnibox-suggestion-empty>'
96+
].join('');
97+
5798
/* eslint-enable indent */
5899

59100
describe('ngcOmnibox.angularComponent.ngcModifySuggestionsTemplateFactory', () => {
60101
const templateCache = {put: () => {}};
61102
let ngcModifySuggestionsTemplate;
62103

63-
it('should modify an un-categorized element', () => {
104+
it('should modify an un-categorized subcomponent', () => {
64105
const elementTemplate =
65106
`<ngc-omnibox-suggestions>${unCategorizedTemplate}</ngc-omnibox-suggestions>`;
66107
const document = jsdom.jsdom(elementTemplate).defaultView.document;
@@ -70,7 +111,7 @@ describe('ngcOmnibox.angularComponent.ngcModifySuggestionsTemplateFactory', () =
70111
expect(ngcModifySuggestionsTemplate(element)).toBe(unCategorizedTemplateOutput);
71112
});
72113

73-
it('should modify a categorized element', () => {
114+
it('should modify a categorized subcomponent', () => {
74115
const elementTemplate =
75116
`<ngc-omnibox-suggestions>${categorizedTemplate}</ngc-omnibox-suggestions>`;
76117
const document = jsdom.jsdom(elementTemplate).defaultView.document;
@@ -79,4 +120,39 @@ describe('ngcOmnibox.angularComponent.ngcModifySuggestionsTemplateFactory', () =
79120
ngcModifySuggestionsTemplate = ngcModifySuggestionsTemplateFactory([document], templateCache);
80121
expect(ngcModifySuggestionsTemplate(element, 'category-tmpl')).toBe(categorizedTemplateOutput);
81122
});
123+
124+
it('should modify a loading subcomponent', () => {
125+
const elementTemplate =
126+
`<ngc-omnibox-suggestions>${loadingElTemplate}</ngc-omnibox-suggestions>`;
127+
const document = jsdom.jsdom(elementTemplate).defaultView.document;
128+
const element = document.querySelector('ngc-omnibox-suggestions');
129+
130+
ngcModifySuggestionsTemplate = ngcModifySuggestionsTemplateFactory([document], templateCache);
131+
expect(ngcModifySuggestionsTemplate(element)).toBe(loadingElTemplateOutput);
132+
});
133+
134+
it('should modify a no-results subcomponent', () => {
135+
const elementTemplate =
136+
`<ngc-omnibox-suggestions>${noResultsElTemplate}</ngc-omnibox-suggestions>`;
137+
const document = jsdom.jsdom(elementTemplate).defaultView.document;
138+
const element = document.querySelector('ngc-omnibox-suggestions');
139+
140+
ngcModifySuggestionsTemplate = ngcModifySuggestionsTemplateFactory([document], templateCache);
141+
expect(ngcModifySuggestionsTemplate(element)).toBe(noResultsElTemplateOutput);
142+
});
143+
144+
it('should only allow one instance of a subcomponent', () => {
145+
const elementTemplate = `
146+
<ngc-omnibox-suggestions>
147+
<ngc-omnibox-suggestion-item></ngc-omnibox-suggestion-item>
148+
<ngc-omnibox-suggestion-item></ngc-omnibox-suggestion-item>
149+
</ngc-omnibox-suggestions>
150+
`;
151+
const document = jsdom.jsdom(elementTemplate).defaultView.document;
152+
const element = document.querySelector('ngc-omnibox-suggestions');
153+
154+
ngcModifySuggestionsTemplate = ngcModifySuggestionsTemplateFactory([document], templateCache);
155+
expect(() => ngcModifySuggestionsTemplate(element))
156+
.toThrowError('Cannot include more than one instance of \'ngc-omnibox-suggestion-item\'');
157+
});
82158
});

src/angularComponent/ngcModifySuggestionsTemplateFactory.js

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,10 @@ export default function ngcModifySuggestionsTemplateFactory($document, $template
1414
templateCacheName = `category-tmpl-${new Date().getTime() * Math.random()}`) {
1515
const doc = $document[0];
1616

17-
const categoryEl = element.querySelector(
18-
'ngc-omnibox-suggestion-category, [ngc-omnibox-suggestion-category]'
19-
);
20-
const itemEl = element.querySelector(
21-
'ngc-omnibox-suggestion-item, [ngc-omnibox-suggestion-item]'
22-
);
23-
const loadingEl = element.querySelector(
24-
'ngc-omnibox-suggestion-loading, [ngc-omnibox-suggestion-loading]'
25-
);
26-
const noResultsEl = element.querySelector(
27-
'ngc-omnibox-suggestion-empty, [ngc-omnibox-suggestion-empty]'
28-
);
17+
const categoryEl = getSubcomponent(element, 'ngc-omnibox-suggestion-category');
18+
const itemEl = getSubcomponent(element, 'ngc-omnibox-suggestion-item');
19+
const loadingEl = getSubcomponent(element, 'ngc-omnibox-suggestion-loading');
20+
const noResultsEl = getSubcomponent(element, 'ngc-omnibox-suggestion-empty');
2921

3022
element.setAttribute('role', 'listbox');
3123

@@ -54,9 +46,7 @@ export default function ngcModifySuggestionsTemplateFactory($document, $template
5446

5547
itemEl.parentNode.appendChild(itemChildrenEl);
5648

57-
const itemHeader = element.querySelector(
58-
'ngc-omnibox-suggestion-header, [ngc-omnibox-suggestion-header]'
59-
);
49+
const itemHeader = getSubcomponent(element, 'ngc-omnibox-suggestion-header');
6050
if (itemHeader) {
6151
itemHeader.setAttribute('suggestion', 'suggestion');
6252
itemHeader.setAttribute('ng-attr-aria-selected',
@@ -98,3 +88,24 @@ export default function ngcModifySuggestionsTemplateFactory($document, $template
9888
}
9989
};
10090
}
91+
92+
/**
93+
* Queries a container for an element reference of a subcomponent, either via a custom element or
94+
* via an attribute.
95+
*
96+
* @private
97+
* @param {HTMLElement} container
98+
* @param {String} directive
99+
* @returns {HTMLElement}
100+
*/
101+
function getSubcomponent(container, directive) {
102+
const element = container.querySelectorAll(`${directive}, [${directive}]`);
103+
104+
if (element.length > 1) {
105+
throw new Error('Cannot include more than one instance of \'' + directive + '\'');
106+
} else if (!element.length) {
107+
return null;
108+
}
109+
110+
return element[0];
111+
}

0 commit comments

Comments
 (0)