Skip to content

Commit 9a9afa0

Browse files
Merge pull request #55 from jonathannewman/maint/main/update-ember-512
(maint) update to ember 5.12, modernize components and tests
2 parents 19381c7 + de0b1ae commit 9a9afa0

27 files changed

+2702
-2450
lines changed

CONTRIBUTING.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,25 @@
22

33
## Installation
44

5-
* `git clone <repository-url>`
6-
* `cd puppet-ivy-tabs`
7-
* `yarn install`
5+
- `git clone <repository-url>`
6+
- `cd puppet-ivy-tabs`
7+
- `yarn install`
88

99
## Linting
1010

11-
* `yarn lint`
12-
* `yarn lint:fix`
11+
- `yarn lint`
12+
- `yarn lint:fix`
1313

1414
## Running tests
1515

16-
* `yarn test` – Runs the test suite on the current Ember version
17-
* `yarn test:ember --server` – Runs the test suite in "watch mode"
18-
* `yarn test:ember-compatibility` – Runs the test suite against multiple Ember versions
16+
- `yarn test` – Runs the test suite on the current Ember version
17+
- `yarn test:ember --server` – Runs the test suite in "watch mode"
18+
- `yarn test:ember-compatibility` – Runs the test suite against multiple Ember versions
1919

2020
## Running the dummy application
2121

22-
* `yarn start`
23-
* Visit the dummy application at [http://localhost:4200](http://localhost:4200).
22+
- `yarn start`
23+
- Visit the dummy application at [http://localhost:4200](http://localhost:4200).
2424

2525
For more information on using ember-cli, visit [https://cli.emberjs.com/release/](https://cli.emberjs.com/release/).
2626

README.md

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,20 @@
11
# @puppet/ivy-tabs
2-
3-
[![Build Status](https://travis-ci.org/IvyApp/ivy-tabs.svg?branch=master)](https://travis-ci.org/IvyApp/ivy-tabs)
4-
[![Dependabot Status](https://api.dependabot.com/badges/status?host=github&repo=IvyApp/ivy-tabs)](https://dependabot.com)
5-
[![Ember Observer Score](http://emberobserver.com/badges/ivy-tabs.svg)](http://emberobserver.com/addons/ivy-tabs)
6-
72
A group of Ember.js Components that interact to create a [WAI-ARIA tab] interface.
83

94
Special thanks to [ic-tabs], which this addon is based on.
105
## Compatibility
116

12-
* Ember.js v4.12 or above
13-
* Ember CLI v4.12 or above
14-
* Node.js v18 or above
7+
- Ember.js v4.12 or above
8+
- Ember CLI v4.12 or above
9+
- Node.js v18 or above
1510

1611
## Installation
1712

1813
```sh
1914
$ ember install ivy-tabs
2015
```
16+
ember install @puppet/ivy-tabs
17+
```
2118
2219
## Usage
2320
@@ -93,7 +90,6 @@ override the `activeClass` property of your ivy-tabs-tabpanel):
9390
}
9491
```
9592

96-
9793
## Contributing
9894

9995
Fork this repo, make a new branch, and send a pull request. Make sure your

addon/components/ivy-tabs-tab.hbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
role="tab"
88
href="{{this.href}}"
99
{{on "click" this.handleClick}}
10+
{{this.registerWithTabList}}
1011
...attributes>
1112
{{~yield~}}
1213
</a>

addon/components/ivy-tabs-tab.js

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Component from '@glimmer/component';
2-
import { once } from '@ember/runloop';
32
import { action } from '@ember/object';
3+
import { modifier } from 'ember-modifier';
4+
import { runTask } from 'ember-lifeline';
45

56
/**
67
* @module ivy-tabs
@@ -14,16 +15,14 @@ let ivyTabsTabCount = 0;
1415
* @extends Ember.Component
1516
*/
1617
export default class IvyTabsTabComponent extends Component {
17-
_registerWithTabList() {
18-
this.args.tabList.registerTab(this);
19-
if (this.isSelected) {
20-
this.select();
21-
}
22-
}
23-
24-
_unregisterWithTabList() {
25-
this.args.tabList.unregisterTab(this);
26-
}
18+
registerWithTabList = modifier(() => {
19+
runTask(this, () => {
20+
this.args.tabList.registerTab(this);
21+
});
22+
return () => {
23+
this.args.tabList.unregisterTab(this);
24+
};
25+
});
2726

2827
/**
2928
* Tells screenreaders which panel this tab controls.
@@ -85,7 +84,6 @@ export default class IvyTabsTabComponent extends Component {
8584
constructor() {
8685
super(...arguments);
8786
this.uniqueSelector = `ivy-tabs-tab-${ivyTabsTabCount++}`;
88-
once(this, this._registerWithTabList);
8987
}
9088

9189
/**
@@ -106,7 +104,11 @@ export default class IvyTabsTabComponent extends Component {
106104
@action
107105
select() {
108106
const onSelect = this.args.onSelect;
109-
if (!this.isDestroying && typeof onSelect === 'function') {
107+
if (
108+
!this.isDestroying &&
109+
!this.isDestroyed &&
110+
typeof onSelect === 'function'
111+
) {
110112
onSelect(this.args.model);
111113
}
112114
}
@@ -189,9 +191,4 @@ export default class IvyTabsTabComponent extends Component {
189191
}
190192
return null;
191193
}
192-
193-
willDestroy() {
194-
super.willDestroy(...arguments);
195-
once(this, this._unregisterWithTabList);
196-
}
197194
}

addon/components/ivy-tabs-tablist.hbs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
aria-label={{if this.effectiveAriaLabel this.effectiveAriaLabel}}
1010
aria-multiselectable={{this.isMultiSelectable}}
1111
...attributes
12-
{{on "keydown" this.keyDown}}>
12+
{{on "keydown" this.keyDown}}
13+
{{this.registerWithTabsContainer}}>
1314

1415
{{~yield (hash tab=(component "ivy-tabs-tab" tabList=this))~}}
1516
</ul>
@@ -22,7 +23,8 @@
2223
aria-label={{if this.effectiveAriaLabel this.effectiveAriaLabel}}
2324
aria-multiselectable={{this.isMultiSelectable}}
2425
...attributes
25-
{{on "keydown" this.keyDown}}>
26+
{{on "keydown" this.keyDown}}
27+
{{this.registerWithTabsContainer}}>
2628

2729
{{~yield (hash tab=(component "ivy-tabs-tab" tabList=this))~}}
2830
</div>

addon/components/ivy-tabs-tablist.js

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import Component from '@glimmer/component';
2-
import { isNone, isEmpty } from '@ember/utils';
2+
import { isNone } from '@ember/utils';
33
import { action } from '@ember/object';
44
import { tracked } from '@glimmer/tracking';
5-
import { once, scheduleOnce } from '@ember/runloop';
5+
import { modifier } from 'ember-modifier';
6+
import { runTask } from 'ember-lifeline';
67

78
export const DOWN_ARROW = 40;
89
export const LEFT_ARROW = 37;
@@ -21,15 +22,17 @@ let instanceCount = 0;
2122
* @extends Ember.Component
2223
*/
2324
export default class IvyTabsTabListComponent extends Component {
24-
_registerWithTabsContainer() {
25-
this.internalId = `ivy-tabs-list-${instanceCount++}`;
25+
registerWithTabsContainer = modifier(() => {
2626
this.args.tabsContainer.registerTabList(this);
27-
once(this, this.selectTab);
28-
}
29-
30-
_unregisterWithTabsContainer() {
31-
this.args.tabsContainer.unregisterTabList(this);
32-
}
27+
// if none of the tabs are selected, try to select one
28+
let selected = this.tabs.find((tab) => tab.isSelected);
29+
if (!selected && this.tabs.length > 0) {
30+
this.selectTab();
31+
}
32+
return () => {
33+
this.args.tabsContainer.unregisterTabList(this);
34+
};
35+
});
3336

3437
/**
3538
* Tells screenreaders that only one tab can be selected at a time.
@@ -71,17 +74,20 @@ export default class IvyTabsTabListComponent extends Component {
7174
*
7275
* @method focusSelectedTab
7376
*/
77+
@action
7478
focusSelectedTab() {
75-
this.selectedTab.focus();
79+
if (!this.isDestroyed && !this.isDestroying) {
80+
this.selectedTab.focus();
81+
}
7682
}
7783

7884
constructor() {
7985
super(...arguments);
80-
this._registerWithTabsContainer();
86+
this.internalId = `ivy-tabs-list-${instanceCount++}`;
8187
}
8288

8389
get isEmpty() {
84-
return isEmpty(this.tabs);
90+
return this.tabs.length === 0;
8591
}
8692

8793
/**
@@ -108,7 +114,7 @@ export default class IvyTabsTabListComponent extends Component {
108114
}
109115

110116
event.preventDefault();
111-
scheduleOnce('afterRender', this, this.focusSelectedTab);
117+
runTask(this, this.focusSelectedTab);
112118
}
113119

114120
/**
@@ -117,16 +123,20 @@ export default class IvyTabsTabListComponent extends Component {
117123
* @method registerTab
118124
* @param {IvyTabs.IvyTabComponent} tab
119125
*/
126+
@action
120127
registerTab(tab) {
121-
this.tabs = this.tabs.concat(tab);
122-
once(this, this.selectTab);
128+
runTask(this, () => {
129+
this.tabs = this.tabs.concat(tab);
130+
this.selectTab();
131+
});
123132
}
124133

125134
/**
126135
* Selects the next tab in the list, if any.
127136
*
128137
* @method selectNextTab
129138
*/
139+
@action
130140
selectNextTab() {
131141
const selectedTab = this.selectedTab;
132142
const tabs = this.tabs;
@@ -155,6 +165,7 @@ export default class IvyTabsTabListComponent extends Component {
155165
*
156166
* @method selectPreviousTab
157167
*/
168+
@action
158169
selectPreviousTab() {
159170
const selectedTab = this.selectedTab;
160171
const tabs = this.tabs;
@@ -194,6 +205,7 @@ export default class IvyTabsTabListComponent extends Component {
194205
return this.args['aria-label'];
195206
}
196207

208+
@action
197209
selectTab() {
198210
const selection = this.selection;
199211

@@ -213,18 +225,20 @@ export default class IvyTabsTabListComponent extends Component {
213225
selectTabByIndex(index) {
214226
const tab = this.tabs[index];
215227

216-
if (tab) {
217-
tab.select();
228+
if (tab && tab.isSelected === false) {
229+
runTask(tab, () => {
230+
tab.select();
231+
});
218232
}
219233
}
220234

221235
selectTabByModel(model) {
222-
const tab = this.tabs.find((element) => {
223-
return element.model === model;
224-
});
236+
const tab = this.tabs.find((element) => element.model === model);
225237

226238
if (tab) {
227-
tab.select();
239+
runTask(tab, () => {
240+
tab.select();
241+
});
228242
}
229243
}
230244

@@ -253,8 +267,7 @@ export default class IvyTabsTabListComponent extends Component {
253267
*/
254268
unregisterTab(tab) {
255269
const index = tab.index;
256-
257-
if (tab.isSelected) {
270+
if (tab.isSelected && !this.isDestroying && !this.isDestroyed) {
258271
if (index === 0) {
259272
this.selectNextTab();
260273
} else {
@@ -266,9 +279,4 @@ export default class IvyTabsTabListComponent extends Component {
266279
return element !== tab;
267280
});
268281
}
269-
270-
willDestroy() {
271-
super.willDestroy(...arguments);
272-
this._unregisterWithTabsContainer();
273-
}
274282
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
<div class="ivy-tabs-tabpanel {{this.active}}" aria-hidden={{this.ariaHidden}} aria-labelledby={{this.ariaLabelledby}} role={{this.ariaRole}} tabindex={{if this.isSelected "0"}} id={{this.elementId}} ...attributes>
1+
<div class="ivy-tabs-tabpanel {{this.active}}" aria-hidden={{this.ariaHidden}} aria-labelledby={{this.ariaLabelledby}} role={{this.ariaRole}} tabindex={{if this.isSelected "0"}} id={{this.elementId}} {{this.registerWithTabsContainer}} ...attributes>
22
{{yield}}
33
</div>

addon/components/ivy-tabs-tabpanel.js

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Component from '@glimmer/component';
2-
import { once } from '@ember/runloop';
2+
import { runTask } from 'ember-lifeline';
3+
import { modifier } from 'ember-modifier';
34

45
/**
56
* @module ivy-tabs
@@ -13,13 +14,14 @@ let instanceCount = 0;
1314
* @extends Ember.Component
1415
*/
1516
export default class IvyTabsPanelComponent extends Component {
16-
_registerWithTabsContainer() {
17-
this.args.tabsContainer.registerTabPanel(this);
18-
}
19-
20-
_unregisterWithTabsContainer() {
21-
this.args.tabsContainer.unregisterTabPanel(this);
22-
}
17+
registerWithTabsContainer = modifier(() => {
18+
runTask(this, () => {
19+
this.args.tabsContainer.registerTabPanel(this);
20+
});
21+
return () => {
22+
this.args.tabsContainer.unregisterTabPanel(this);
23+
};
24+
});
2325

2426
/**
2527
* Accessed as a className binding to apply the panel's `activeClass` CSS
@@ -89,7 +91,6 @@ export default class IvyTabsPanelComponent extends Component {
8991
constructor() {
9092
super(...arguments);
9193
this.internalId = `ivy-tabs-panel-${instanceCount++}`;
92-
once(this, this._registerWithTabsContainer);
9394
}
9495

9596
/**
@@ -133,9 +134,4 @@ export default class IvyTabsPanelComponent extends Component {
133134
}
134135
return [];
135136
}
136-
137-
willDestroy() {
138-
super.willDestroy(...arguments);
139-
once(this, this._unregisterWithTabsContainer);
140-
}
141137
}

0 commit comments

Comments
 (0)