Skip to content

Commit 4519e32

Browse files
surmarobdodson
authored andcommitted
Clean up howto-tabs (#100)
* Clean up howto-tabs * Rename elements
1 parent b2670da commit 4519e32

File tree

6 files changed

+80
-84
lines changed

6 files changed

+80
-84
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM node:7.6.0-onbuild
1+
FROM node:8-onbuild
22

33
RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
44
RUN sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'

elements/howto-tabs/demo.html

Lines changed: 12 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,48 +11,32 @@
1111
limitations under the License.
1212
-->
1313
<style>
14-
howto-tabs {
15-
/**
16-
* The element uses flex box to line up the tabs in the first line
17-
* and wraps if necessary.
18-
*/
19-
display: flex;
20-
flex-wrap: wrap;
21-
}
22-
howto-tabs-tab {
14+
howto-tab {
2315
border: 1px solid black;
2416
padding: 20px;
2517
}
26-
howto-tabs-panel {
27-
/**
28-
* Each panel has a base size of 100%, forcing it to be in its
29-
* own row.
30-
*/
31-
flex-basis: 100%;
18+
howto-panel {
3219
padding: 20px;
3320
background-color: lightgray;
3421
}
35-
howto-tabs-panel[hidden] {
36-
display: none;
37-
}
38-
howto-tabs-tab[selected] {
22+
howto-tab[selected] {
3923
background-color: bisque;
4024
}
4125

4226
/**
43-
* If JavaScript does not run, the element will stay `:unresolved`.
27+
* If JavaScript does not run, the element will not match `:defined`.
4428
* In that case this style adds spacing between tabs and previous panel.
4529
*/
46-
howto-tabs:unresolved howto-tabs-tab:unresolved {
47-
margin-top: 10px;
30+
howto-tabs:not(:defined), howto-tab:not(:defined), howto-panel:not(:defined) {
31+
display: block;
4832
}
4933
</style>
5034

5135
<howto-tabs>
52-
<howto-tabs-tab role="heading" slot="tab">Tab 1</howto-tabs-tab>
53-
<howto-tabs-panel role="region" slot="panel">Content 1</howto-tabs-panel>
54-
<howto-tabs-tab role="heading" slot="tab">Tab 2</howto-tabs-tab>
55-
<howto-tabs-panel role="region" slot="panel">Content 2</howto-tabs-panel>
56-
<howto-tabs-tab role="heading" slot="tab">Tab 3</howto-tabs-tab>
57-
<howto-tabs-panel role="region" slot="panel">Content 3</howto-tabs-panel>
36+
<howto-tab role="heading" slot="tab">Tab 1</howto-tab>
37+
<howto-panel role="region" slot="panel">Content 1</howto-panel>
38+
<howto-tab role="heading" slot="tab">Tab 2</howto-tab>
39+
<howto-panel role="region" slot="panel">Content 2</howto-panel>
40+
<howto-tab role="heading" slot="tab">Tab 3</howto-tab>
41+
<howto-panel role="region" slot="panel">Content 3</howto-panel>
5842
</howto-tabs>

elements/howto-tabs/howto-tabs.e2etest.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,10 @@ describe('howto-tabs', function() {
9292

9393
await this.driver.executeScript(_ => {
9494
window.tabpanel = document.querySelector('howto-tabs');
95-
window.newTab = document.createElement('howto-tabs-tab');
95+
window.newTab = document.createElement('howto-tab');
9696
newTab.slot = 'tab';
9797
newTab.textContent = 'New Tab';
98-
window.newPanel = document.createElement('howto-tabs-panel');
98+
window.newPanel = document.createElement('howto-panel');
9999
newPanel.slot = 'panel';
100100
newPanel.textContent = 'Some content';
101101
tabpanel.appendChild(newTab);
@@ -131,7 +131,7 @@ describe('howto-tabs pre-upgrade', function() {
131131

132132
it('should handle attributes set before upgrade', async function() {
133133
await this.driver.executeScript(_ => {
134-
window.lastTab = document.querySelector('howto-tabs > howto-tabs-tab:last-of-type');
134+
window.lastTab = document.querySelector('howto-tabs > howto-tab:last-of-type');
135135
window.lastPanel = lastTab.nextElementSibling;
136136

137137
window.lastTab.setAttribute('selected', '');
@@ -147,7 +147,7 @@ describe('howto-tabs pre-upgrade', function() {
147147

148148
it('should handle instance properties set before upgrade', async function() {
149149
await this.driver.executeScript(_ => {
150-
window.lastTab = document.querySelector('howto-tabs > howto-tabs-tab:last-of-type');
150+
window.lastTab = document.querySelector('howto-tabs > howto-tab:last-of-type');
151151
window.lastPanel = lastTab.nextElementSibling;
152152

153153
window.lastTab.selected = true;

elements/howto-tabs/howto-tabs.js

Lines changed: 49 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,19 @@
2727
};
2828

2929
// To avoid invoking the parser with `.innerHTML` for every new instance, a
30-
// template for the contents of the ShadowDOM is is shared by all
30+
// template for the contents of the ShadowDOM is shared by all
3131
// `<howto-tabs>` instances.
32-
const shadowDOMTemplate = document.createElement('template');
33-
shadowDOMTemplate.innerHTML = `
32+
const template = document.createElement('template');
33+
template.innerHTML = `
34+
<style>
35+
:host {
36+
display: flex;
37+
flex-wrap: wrap;
38+
}
39+
::slotted(howto-panel) {
40+
flex-basis: 100%;
41+
}
42+
</style>
3443
<slot name="tab"></slot>
3544
<slot name="panel"></slot>
3645
`;
@@ -56,11 +65,15 @@
5665
// using slots.
5766
this.attachShadow({mode: 'open'});
5867
// Import the shared template to create the slots for tabs and panels.
59-
this.shadowRoot.appendChild(
60-
document.importNode(shadowDOMTemplate.content, true)
61-
);
68+
this.shadowRoot.appendChild(template.content.cloneNode(true));
69+
6270
this._tabSlot = this.shadowRoot.querySelector('slot[name=tab]');
6371
this._panelSlot = this.shadowRoot.querySelector('slot[name=panel]');
72+
73+
// This element needs to react to new children as it links up tabs and
74+
// panel semantically using `aria-labelledby` and `aria-controls`.
75+
// New children will get slotted automatically and cause `slotchange`
76+
// to fire, so not `MutationObserver` is needed.
6477
this._tabSlot.addEventListener('slotchange', this._onSlotChange);
6578
this._panelSlot.addEventListener('slotchange', this._onSlotChange);
6679
}
@@ -78,19 +91,26 @@
7891
if (!this.hasAttribute('role'))
7992
this.setAttribute('role', 'tablist');
8093

81-
// Currently, `slotchange` does not fire when an element is upgraded. For
82-
// this reason, the element always processes the slots after the inner
83-
// elements have been defined. If the current behavior of the `slotchange`
84-
// event is change (as proposed in
85-
// [this issue](https://github.com/whatwg/dom/issues/447)), the code below
86-
// can be removed.
94+
// Up until recently, `slotchange` events did not fire when an element is
95+
// upgraded by the parser. For this reason, the element invokes the
96+
// handler manually. Once the new behavior lands in all browsers, the code
97+
// below can be removed.
8798
Promise.all([
88-
customElements.whenDefined('howto-tabs-tab'),
89-
customElements.whenDefined('howto-tabs-panel'),
99+
customElements.whenDefined('howto-tab'),
100+
customElements.whenDefined('howto-panel'),
90101
])
91102
.then(_ => this._linkPanels());
92103
}
93104

105+
/**
106+
* `disconnectedCallback` removes the event listeners that
107+
* `connectedCallback` added.
108+
*/
109+
disconnectedCallback() {
110+
this.removeEventListener('keydown', this._onKeyDown);
111+
this.removeEventListener('click', this._onClick);
112+
}
113+
94114
/**
95115
* `_onSlotChange` is called whenever an element is added or removed from
96116
* one of the ShadowDOM slots.
@@ -114,9 +134,9 @@
114134
// that controls it.
115135
tabs.forEach(tab => {
116136
const panel = tab.nextElementSibling;
117-
if (panel.tagName.toLowerCase() !== 'howto-tabs-panel') {
137+
if (panel.tagName.toLowerCase() !== 'howto-panel') {
118138
console.error(`Tab #${tab.id} is not a` +
119-
`sibling of a <howto-tabs-panel>`);
139+
`sibling of a <howto-panel>`);
120140
return;
121141
}
122142

@@ -144,14 +164,14 @@
144164
* cheap to read.
145165
*/
146166
_allPanels() {
147-
return Array.from(this.querySelectorAll('howto-tabs-panel'));
167+
return Array.from(this.querySelectorAll('howto-panel'));
148168
}
149169

150170
/**
151171
* `_allTabs` returns all the tabs in the tab panel.
152172
*/
153173
_allTabs() {
154-
return Array.from(this.querySelectorAll('howto-tabs-tab'));
174+
return Array.from(this.querySelectorAll('howto-tab'));
155175
}
156176

157177
/**
@@ -215,14 +235,6 @@
215235
panels.forEach(panel => panel.hidden = true);
216236
}
217237

218-
/**
219-
* `disconnectedCallback` removes the event listeners that
220-
* `connectedCallback` added.
221-
*/
222-
disconnectedCallback() {
223-
this.removeEventListener('keydown', this._onKeyDown);
224-
this.removeEventListener('click', this._onClick);
225-
}
226238

227239
/**
228240
* `_selectTab` marks the given tab as selected.
@@ -300,23 +312,23 @@
300312
this._selectTab(event.target);
301313
}
302314
}
303-
window.customElements.define('howto-tabs', HowtoTabs);
315+
customElements.define('howto-tabs', HowtoTabs);
304316

305317
// `howtoTabCounter` counts the number of `<howto-tab>` instances created. The
306318
// number is used to generated new, unique IDs.
307319
let howtoTabCounter = 0;
308320
/**
309-
* `HowtoTabsTab` is a tab for a `<howto-tabs>` tab panel. `<howto-tabs-tab>`
321+
* `HowtoTabsTab` is a tab for a `<howto-tabs>` tab panel. `<howto-tab>`
310322
* should always be used with `role=heading` in the markup so that the
311323
* semantics remain useable when JavaScript is failing.
312324
*
313-
* A `<howto-tabs-tab>` declares which `<howto-tabs=panel>` it belongs to by
325+
* A `<howto-tab>` declares which `<howto-panel>` it belongs to by
314326
* using that panel’s ID as the value for the `aria-controls` attribute.
315327
*
316-
* A `<howto-tabs-tab>` will automatically generate a unique ID if none
328+
* A `<howto-tab>` will automatically generate a unique ID if none
317329
* is specified.
318330
*/
319-
class HowtoTabsTab extends HTMLElement {
331+
class HowtoTab extends HTMLElement {
320332
static get observedAttributes() {
321333
return ['selected'];
322334
}
@@ -330,7 +342,7 @@
330342
// changes its role to `tab`.
331343
this.setAttribute('role', 'tab');
332344
if (!this.id)
333-
this.id = `howto-tabs-tab-generated-${howtoTabCounter++}`;
345+
this.id = `howto-tab-generated-${howtoTabCounter++}`;
334346

335347
// Set a well-defined initial state.
336348
this.setAttribute('aria-selected', 'false');
@@ -387,24 +399,24 @@
387399
return this.hasAttribute('selected');
388400
}
389401
}
390-
window.customElements.define('howto-tabs-tab', HowtoTabsTab);
402+
customElements.define('howto-tab', HowtoTab);
391403

392404
let howtoPanelCounter = 0;
393405
/**
394-
* `HowtoTabsPanel` is a panel for a `<howto-tabs>` tab panel.
406+
* `HowtoPanel` is a panel for a `<howto-tabs>` tab panel.
395407
*/
396-
class HowtoTabsPanel extends HTMLElement {
408+
class HowtoPanel extends HTMLElement {
397409
constructor() {
398410
super();
399411
}
400412

401413
connectedCallback() {
402414
this.setAttribute('role', 'tabpanel');
403415
if (!this.id)
404-
this.id = `howto-tabs-panel-generated-${howtoPanelCounter++}`;
416+
this.id = `howto-panel-generated-${howtoPanelCounter++}`;
405417
}
406418
}
407-
window.customElements.define('howto-tabs-panel', HowtoTabsPanel);
419+
customElements.define('howto-panel', HowtoPanel);
408420
})();
409421

410422

elements/howto-tabs/howto-tabs.unittest.js

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,31 +24,31 @@ describe('howto-tabs', function() {
2424
beforeEach(function() {
2525
this.container.innerHTML = `
2626
<howto-tabs>
27-
<howto-tabs-tab role="heading">Tab 1</howto-tabs-tab>
28-
<howto-tabs-panel role="region">Content 1</howto-tabs-panel>
29-
<howto-tabs-tab role="heading">Tab 2</howto-tabs-tab>
30-
<howto-tabs-panel role="region">Content 2</howto-tabs-panel>
31-
<howto-tabs-tab role="heading">Tab 3</howto-tabs-tab>
32-
<howto-tabs-panel role="region">Content 3</howto-tabs-panel>
27+
<howto-tab role="heading">Tab 1</howto-tab >
28+
<howto-panel role="region">Content 1</howto-panel>
29+
<howto-tab role="heading">Tab 2</howto-tab >
30+
<howto-panel role="region">Content 2</howto-panel>
31+
<howto-tab role="heading">Tab 3</howto-tab >
32+
<howto-panel role="region">Content 3</howto-panel>
3333
</howto-tabs>
3434
`;
3535
return Promise.all([
3636
howtoComponents.waitForElement('howto-tabs'),
37-
howtoComponents.waitForElement('howto-tabs-tab'),
38-
howtoComponents.waitForElement('howto-tabs-panel'),
37+
howtoComponents.waitForElement('howto-tab'),
38+
howtoComponents.waitForElement('howto-panel'),
3939
]).then(_ => {
4040
this.tabpanel = this.container.querySelector('howto-tabs');
41-
this.tabs = Array.from(this.container.querySelectorAll('howto-tabs-tab'));
42-
this.panels = Array.from(this.container.querySelectorAll('howto-tabs-panel'));
41+
this.tabs = Array.from(this.container.querySelectorAll('howto-tab'));
42+
this.panels = Array.from(this.container.querySelectorAll('howto-panel'));
4343
});
4444
});
4545

4646
it('should know about all the tabs', function() {
47-
expect(this.tabpanel._allTabs()).to.have.length(this.container.querySelectorAll('howto-tabs-tab').length);
47+
expect(this.tabpanel._allTabs()).to.have.length(this.container.querySelectorAll('howto-tab ').length);
4848
});
4949

5050
it('should know about all the panels', function() {
51-
expect(this.tabpanel._allPanels()).to.have.length(this.container.querySelectorAll('howto-tabs-panel').length);
51+
expect(this.tabpanel._allPanels()).to.have.length(this.container.querySelectorAll('howto-panel').length);
5252
});
5353

5454
it('should add `aria-labelledby` to panels', function() {

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
"version": "0.0.1",
44
"description": "",
55
"scripts": {
6-
"build": "mkdir -p docs/scripts && cp node_modules/@webcomponents/custom-elements/custom-elements.min.js docs/scripts/ && cp node_modules/@webcomponents/shadydom/shadydom.min.js docs/scripts && node build-documentation.js",
6+
"build": "mkdir -p docs/scripts && cp node_modules/@webcomponents/custom-elements/custom-elements.min.js docs/scripts/ && cp node_modules/@webcomponents/shadydom/shadydom.min.js docs/scripts && node build-documentation.js",
77
"docker-build": "docker build -t googlechrome/howto-components .",
88
"docker-test": "docker run -e TRAVIS=$TRAVIS googlechrome/howto-components",
99
"docker": "npm run docker-build && npm run docker-test",
1010
"watch": "npm-watch",
1111
"test-lint": "eslint elements site-resources *.js",
1212
"test-unit": "karma start",
13-
"test-e2e": "node --harmony-async-await run-e2e-tests.js",
13+
"test-e2e": "node run-e2e-tests.js",
1414
"test": "npm run test-lint && npm run test-unit && npm run test-e2e"
1515
},
1616
"watch": {

0 commit comments

Comments
 (0)