Skip to content

Commit 62d9d02

Browse files
Drop ember-concurrency 1.x support, fix ember-keyboard deprecations (#854)
* Start fixing ember-keyboard deprecations * Fix import * Refactor away from keyboard mixins * Fix lint * Drop ember-concurrency 1.x support * Update ember-concurrency
1 parent eca8dd8 commit 62d9d02

File tree

11 files changed

+531
-236
lines changed

11 files changed

+531
-236
lines changed

.github/workflows/ci-cd.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,6 @@ jobs:
130130
- 'ember-canary'
131131
- 'ember-default-with-jquery'
132132
- 'ember-classic'
133-
- 'ember-concurrency-1.x'
134133
- 'ember-concurrency-2.x'
135134
timeout-minutes: 7
136135
steps:
Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,46 @@
11
import Component from '@ember/component';
2-
import layout from './template';
3-
import { EKMixin, keyUp } from 'ember-keyboard';
4-
import { on } from '@ember/object/evented';
2+
import template from './template';
53
import { task } from 'ember-concurrency';
64
import config from 'ember-get-config';
5+
import { action } from '@ember/object';
76
import { inject as service } from '@ember/service';
7+
import { classNames, layout } from '@ember-decorators/component';
88
import { formElementHasFocus } from 'ember-cli-addon-docs/keyboard-config';
99

1010
const projectName = config['ember-cli-addon-docs'].projectName;
1111

12-
export default Component.extend(EKMixin, {
13-
layout,
14-
store: service(),
12+
@classNames('docs-ml-auto')
13+
@layout(template)
14+
export default class DocsHeaderSearchBoxComponent extends Component {
15+
@service store;
1516

16-
classNames: 'docs-ml-auto',
17-
18-
query: null,
19-
20-
keyboardActivated: true,
17+
query = null;
2118

2219
didInsertElement() {
23-
this._super();
20+
super.didInsertElement(...arguments);
2421

2522
this.fetchProject.perform();
26-
},
23+
}
2724

2825
// TODO: The searchbox doesn't work without the project being fetched.
2926
// We should move this logic (and everywhere else in the code that's fetching
3027
// the project) within a new addonDocs service that wires all that up together.
3128
// I think it's fine if our Docs-* components assume there is a single global
3229
// project.
33-
fetchProject: task(function*() {
30+
@task
31+
*fetchProject() {
3432
yield this.store.findRecord('project', projectName);
35-
}),
33+
}
3634

37-
focusSearch: on(keyUp('Slash'), function() {
35+
@action
36+
focusSearch() {
3837
if (!formElementHasFocus()) {
3938
this.element.querySelector('input').focus();
4039
}
41-
}),
40+
}
4241

43-
unfocusSearch: on(keyUp('Escape'), function() {
42+
@action
43+
unfocusSearch() {
4444
this.get('on-input')(null);
45-
})
46-
});
45+
}
46+
}
Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,29 @@
11
<div class="docs-relative docs-text-grey-darkest" data-search-box>
2-
{{svg-jar "search" width=12 height=12 class="docs-absolute docs-top-0 docs-h-full docs-ml-1"}}
2+
{{svg-jar
3+
"search"
4+
width=12
5+
height=12
6+
class="docs-absolute docs-top-0 docs-h-full docs-ml-1"
7+
}}
38

49
{{!--
510
This is a dumb input - so why not use the {{input}} helper? Because apparently
611
it takes over enter and ctrl+n/p keys. We are using those for our own shortcuts
712
so we stick with the dumb html element. Maybe there's a way to disable them
813
and get the convenient two-way binding.
914
--}}
10-
<input oninput={{action @on-input value="target.value"}}
15+
<input
16+
oninput={{action @on-input value="target.value"}}
1117
value={{@query}}
1218
type="text"
1319
disabled={{this.fetchProject.isRunning}}
1420
placeholder="SEARCH"
1521
class="docs-w-24 docs-text-xxs docs-p-2 docs-pl-6 docs-rounded focus:docs-bg-grey-lighter outline-none"
1622
data-search-box-input
1723
data-test-search-box-input
18-
aria-label="search">
24+
aria-label="search"
25+
/>
1926
</div>
27+
28+
{{on-key "/" this.focusSearch event="keyup"}}
29+
{{on-key "Escape" this.unfocusSearch event="keyup"}}
Lines changed: 111 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,150 +1,174 @@
11
import { inject as service } from '@ember/service';
22
import Component from '@ember/component';
3-
import layout from './template';
4-
import { EKMixin, keyUp, keyDown } from 'ember-keyboard';
5-
import { on } from '@ember/object/evented';
3+
import { action } from '@ember/object';
4+
import template from './template';
5+
import { keyResponder, onKey } from 'ember-keyboard';
6+
import { layout } from '@ember-decorators/component';
67
import { computed } from '@ember/object';
78
import { task } from 'ember-concurrency';
89
import config from 'ember-get-config';
910

1011
const projectName = config['ember-cli-addon-docs'].projectName;
1112

12-
export default Component.extend(EKMixin, {
13-
layout,
13+
@keyResponder
14+
@layout(template)
15+
export default class DocsHeaderSearchResultsComponent extends Component {
16+
@service docsSearch;
17+
@service router;
18+
@service store;
1419

15-
docsSearch: service(),
16-
router: service(),
17-
store: service(),
18-
19-
query: null, // passed in
20-
selectedIndex: null,
21-
22-
keyboardActivated: true,
20+
query = null; // passed in
21+
selectedIndex = null;
2322

2423
didInsertElement() {
25-
this._super();
24+
super.didInsertElement(...arguments);
2625

2726
// Start downloading the search index immediately
2827
this.docsSearch.loadSearchIndex();
29-
},
28+
}
3029

3130
didReceiveAttrs() {
32-
this._super(...arguments);
31+
super.didReceiveAttrs(...arguments);
3332

3433
this.search.perform();
35-
},
34+
}
3635

37-
project: computed(function() {
36+
get project() {
3837
return this.store.peekRecord('project', projectName);
39-
}),
38+
}
4039

41-
trimmedQuery: computed('query', function() {
40+
@computed('query')
41+
get trimmedQuery() {
4242
return this.query.trim();
43-
}),
43+
}
4444

45-
search: task(function*() {
45+
@task({ restartable: true })
46+
*search() {
4647
let results;
4748

4849
if (this.trimmedQuery) {
4950
results = yield this.docsSearch.search(this.trimmedQuery);
5051
}
5152

52-
this.set('selectedIndex', (results.length ? 0 : null));
53+
this.set('selectedIndex', results.length ? 0 : null);
5354
this.set('rawSearchResults', results);
54-
}).restartable(),
55+
}
5556

56-
searchResults: computed('project.navigationIndex', 'rawSearchResults.[]', function() {
57+
@computed('project.navigationIndex', 'rawSearchResults.[]')
58+
get searchResults() {
5759
let rawSearchResults = this.rawSearchResults;
5860
let router = this.router;
59-
let routerMicrolib = router._router._routerMicrolib || router._router.router;
61+
let routerMicrolib =
62+
router._router._routerMicrolib || router._router.router;
6063

6164
if (rawSearchResults) {
62-
return this.rawSearchResults
63-
// If the doc has a route, ensure it exists
64-
.filter(({ document }) => {
65-
if (document.route) {
66-
let routeExists = routerMicrolib.recognizer.names[document.route];
67-
68-
return routeExists && document.route !== 'not-found' && document.route !== 'application';
69-
} else {
70-
return true;
71-
}
72-
})
73-
74-
// Filter out the templates of the API items' pages, since we handle them separately
75-
.filter(({ document }) => {
76-
let isApiItemTemplate = (document.route === 'docs.api.item' && document.type === 'template');
77-
return !isApiItemTemplate;
78-
})
79-
80-
// Filter out modules that are not in the navigationIndex
81-
.filter(({ document }) => {
82-
if (document.type === 'module') {
83-
let navigableModules = this.get('project.navigationIndex').find(section => section.type === 'modules');
84-
let navigableModuleIds = navigableModules ? navigableModules.items.map(item => item.id) : [];
85-
86-
return navigableModuleIds.includes(document.title);
87-
} else {
88-
return true;
89-
}
90-
})
91-
92-
// Add a reference to the Ember Data model to each API item search result
93-
.map(searchResult => {
94-
let { document } = searchResult;
95-
if (document.type !== 'template') {
96-
let store = this.store;
97-
searchResult.model = store.peekRecord(document.type, document.item.id)
98-
}
99-
100-
return searchResult;
101-
});
65+
return (
66+
this.rawSearchResults
67+
// If the doc has a route, ensure it exists
68+
.filter(({ document }) => {
69+
if (document.route) {
70+
let routeExists = routerMicrolib.recognizer.names[document.route];
71+
72+
return (
73+
routeExists &&
74+
document.route !== 'not-found' &&
75+
document.route !== 'application'
76+
);
77+
} else {
78+
return true;
79+
}
80+
})
81+
82+
// Filter out the templates of the API items' pages, since we handle them separately
83+
.filter(({ document }) => {
84+
let isApiItemTemplate =
85+
document.route === 'docs.api.item' &&
86+
document.type === 'template';
87+
return !isApiItemTemplate;
88+
})
89+
90+
// Filter out modules that are not in the navigationIndex
91+
.filter(({ document }) => {
92+
if (document.type === 'module') {
93+
let navigableModules = this.get('project.navigationIndex').find(
94+
(section) => section.type === 'modules'
95+
);
96+
let navigableModuleIds = navigableModules
97+
? navigableModules.items.map((item) => item.id)
98+
: [];
99+
100+
return navigableModuleIds.includes(document.title);
101+
} else {
102+
return true;
103+
}
104+
})
105+
106+
// Add a reference to the Ember Data model to each API item search result
107+
.map((searchResult) => {
108+
let { document } = searchResult;
109+
if (document.type !== 'template') {
110+
let store = this.store;
111+
searchResult.model = store.peekRecord(
112+
document.type,
113+
document.item.id
114+
);
115+
}
116+
117+
return searchResult;
118+
})
119+
);
102120
}
103-
}),
104121

105-
gotoSelectedItem: on(keyUp('Enter'), function() {
122+
return undefined;
123+
}
124+
125+
@onKey('Enter', { event: 'keyup' })
126+
gotoSelectedItem() {
106127
if (this.selectedIndex !== null) {
107128
let selectedResult = this.searchResults[this.selectedIndex];
108129
if (selectedResult.document.type === 'template') {
109130
this.router.transitionTo(selectedResult.document.route);
110131
} else {
111-
this.router.transitionTo('docs.api.item', selectedResult.model.get('routingId'));
132+
this.router.transitionTo(
133+
'docs.api.item',
134+
selectedResult.model.get('routingId')
135+
);
112136
}
113137
}
114138

115139
this.get('on-visit')();
116-
}),
140+
}
117141

118-
nextSearchResult: on(keyDown('ctrl+KeyN'), keyDown('ArrowDown'), function() {
142+
@onKey('ctrl+KeyN')
143+
@onKey('ArrowDown')
144+
nextSearchResult() {
119145
let hasSearchResults = this.get('searchResults.length');
120-
let lastResultIsSelected = (this.selectedIndex + 1 === this.get('searchResults.length'));
146+
let lastResultIsSelected =
147+
this.selectedIndex + 1 === this.get('searchResults.length');
121148

122149
if (hasSearchResults && !lastResultIsSelected) {
123150
this.incrementProperty('selectedIndex');
124151
}
125-
}),
152+
}
126153

127-
previousSearchResult: on(keyDown('ctrl+KeyP'), keyDown('ArrowUp'), function() {
154+
@onKey('ctrl+KeyP')
155+
@onKey('ArrowUp')
156+
previousSearchResult() {
128157
let hasSearchResults = this.get('searchResults.length');
129-
let firstResultIsSelected = (this.selectedIndex === 0);
158+
let firstResultIsSelected = this.selectedIndex === 0;
130159

131160
if (hasSearchResults && !firstResultIsSelected) {
132161
this.decrementProperty('selectedIndex');
133162
}
134-
}),
163+
}
135164

165+
@action
136166
clearSearch() {
137167
this.set('query', null);
138-
},
139-
140-
actions: {
141-
selectResult(index) {
142-
this.set('selectedIndex', index);
143-
},
144-
145-
clearSearch() {
146-
this.clearSearch();
147-
},
148168
}
149169

150-
});
170+
@action
171+
selectResult(index) {
172+
this.set('selectedIndex', index);
173+
}
174+
}

0 commit comments

Comments
 (0)