Skip to content
This repository was archived by the owner on Jan 13, 2025. It is now read-only.

Commit 4675c95

Browse files
authored
feat(auto-init): Convert JS to TypeScript (#4395)
Refs #4225
1 parent b8b1988 commit 4675c95

File tree

4 files changed

+66
-24
lines changed

4 files changed

+66
-24
lines changed

packages/mdc-auto-init/index.js renamed to packages/mdc-auto-init/index.ts

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,30 @@
2121
* THE SOFTWARE.
2222
*/
2323

24-
const registry = Object.create(null);
24+
// tslint:disable:only-arrow-functions
2525

26-
const CONSOLE_WARN = console.warn.bind(console);
26+
import {MDCComponent, MDCFoundation} from '@material/base/index';
2727

28-
function _emit(evtType, evtData, shouldBubble = false) {
28+
interface ComponentClass {
29+
// tslint:disable-next-line:no-any a component can pass in anything it needs to the constructor
30+
new<F extends MDCFoundation>(root: Element, foundation?: F, ...args: any[]): MDCComponent<F>;
31+
attachTo<F extends MDCFoundation>(root: Element): MDCComponent<F>;
32+
}
33+
34+
interface Registry {
35+
[key: string]: ComponentClass;
36+
}
37+
38+
const registry: Registry = {};
39+
40+
const CONSOLE_WARN = console.warn.bind(console); // tslint:disable-line:no-console
41+
42+
function _emit<T extends object>(evtType: string, evtData: T, shouldBubble = false) {
2943
let evt;
3044
if (typeof CustomEvent === 'function') {
31-
evt = new CustomEvent(evtType, {
32-
detail: evtData,
45+
evt = new CustomEvent<T>(evtType, {
3346
bubbles: shouldBubble,
47+
detail: evtData,
3448
});
3549
} else {
3650
evt = document.createEvent('CustomEvent');
@@ -40,36 +54,39 @@ function _emit(evtType, evtData, shouldBubble = false) {
4054
document.dispatchEvent(evt);
4155
}
4256

57+
/* istanbul ignore next: optional argument is not a branch statement */
4358
/**
44-
* Auto-initializes all mdc components on a page.
59+
* Auto-initializes all MDC components on a page.
4560
*/
46-
export default function mdcAutoInit(root = document, warn = CONSOLE_WARN) {
61+
export function mdcAutoInit(root = document, warn = CONSOLE_WARN) {
4762
const components = [];
48-
const nodes = root.querySelectorAll('[data-mdc-auto-init]');
49-
for (let i = 0, node; (node = nodes[i]); i++) {
50-
const ctorName = node.dataset.mdcAutoInit;
63+
const nodes: Element[] = [].slice.call(root.querySelectorAll('[data-mdc-auto-init]'));
64+
65+
for (const node of nodes) {
66+
const ctorName = node.getAttribute('data-mdc-auto-init');
5167
if (!ctorName) {
5268
throw new Error('(mdc-auto-init) Constructor name must be given.');
5369
}
5470

55-
const Ctor = registry[ctorName];
56-
if (typeof Ctor !== 'function') {
71+
const Constructor = registry[ctorName]; // tslint:disable-line:variable-name
72+
if (typeof Constructor !== 'function') {
5773
throw new Error(
5874
`(mdc-auto-init) Could not find constructor in registry for ${ctorName}`);
5975
}
6076

61-
if (node[ctorName]) {
77+
if (Object.getOwnPropertyDescriptor(node, ctorName)) {
6278
warn(`(mdc-auto-init) Component already initialized for ${node}. Skipping...`);
6379
continue;
6480
}
6581

6682
// TODO: Should we make an eslint rule for an attachTo() static method?
67-
const component = Ctor.attachTo(node);
83+
// See https://github.com/Microsoft/TypeScript/issues/14600 for discussion of static interface support in TS
84+
const component = Constructor.attachTo(node);
6885
Object.defineProperty(node, ctorName, {
86+
configurable: true,
87+
enumerable: false,
6988
value: component,
7089
writable: false,
71-
enumerable: false,
72-
configurable: true,
7390
});
7491
components.push(component);
7592
}
@@ -78,22 +95,27 @@ export default function mdcAutoInit(root = document, warn = CONSOLE_WARN) {
7895
return components;
7996
}
8097

81-
mdcAutoInit.register = function(componentName, Ctor, warn = CONSOLE_WARN) {
82-
if (typeof Ctor !== 'function') {
83-
throw new Error(`(mdc-auto-init) Invalid Ctor value ${Ctor}. Expected function`);
98+
// Constructor is PascalCased because it is a direct reference to a class, rather than an instance of a class.
99+
// tslint:disable-next-line:variable-name
100+
mdcAutoInit.register = function(componentName: string, Constructor: ComponentClass, warn = CONSOLE_WARN) {
101+
if (typeof Constructor !== 'function') {
102+
throw new Error(`(mdc-auto-init) Invalid Ctor value ${Constructor}. Expected function`);
84103
}
85104
if (registry[componentName]) {
86105
warn(
87-
`(mdc-auto-init) Overriding registration for ${componentName} with ${Ctor}. ` +
106+
`(mdc-auto-init) Overriding registration for ${componentName} with ${Constructor}. ` +
88107
`Was: ${registry[componentName]}`);
89108
}
90-
registry[componentName] = Ctor;
109+
registry[componentName] = Constructor;
91110
};
92111

93-
mdcAutoInit.deregister = function(componentName) {
112+
mdcAutoInit.deregister = function(componentName: string) {
94113
delete registry[componentName];
95114
};
96115

97116
mdcAutoInit.deregisterAll = function() {
98-
Object.keys(registry).forEach(this.deregister, this);
117+
const keys = Object.keys(registry) as string[];
118+
keys.forEach(this.deregister, this);
99119
};
120+
121+
export default mdcAutoInit;

packages/mdc-auto-init/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,8 @@
77
"repository": {
88
"type": "git",
99
"url": "https://github.com/material-components/material-components-web.git"
10+
},
11+
"dependencies": {
12+
"@material/base": "^0.41.0"
1013
}
1114
}

scripts/webpack/js-bundle-factory.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ class JsBundleFactory {
153153
bundleName: 'main-js-a-la-carte',
154154
chunks: {
155155
animation: getAbsolutePath('/packages/mdc-animation/index.ts'),
156-
autoInit: getAbsolutePath('/packages/mdc-auto-init/index.js'),
156+
autoInit: getAbsolutePath('/packages/mdc-auto-init/index.ts'),
157157
base: getAbsolutePath('/packages/mdc-base/index.ts'),
158158
checkbox: getAbsolutePath('/packages/mdc-checkbox/index.ts'),
159159
chips: getAbsolutePath('/packages/mdc-chips/index.ts'),

test/unit/mdc-auto-init/mdc-auto-init.test.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ class FakeComponent {
3636
}
3737
}
3838

39+
class InvalidComponent {
40+
constructor(node) {
41+
this.node = node;
42+
}
43+
}
44+
3945
const createFixture = () => bel`
4046
<div id="root">
4147
<p data-mdc-auto-init="FakeComponent" class="mdc-fake">Fake Element</p>
@@ -48,6 +54,12 @@ const setupTest = () => {
4854
return createFixture();
4955
};
5056

57+
const setupInvalidTest = () => {
58+
mdcAutoInit.deregisterAll();
59+
mdcAutoInit.register('InvalidComponent', InvalidComponent);
60+
return createFixture();
61+
};
62+
5163
suite('MDCAutoInit');
5264

5365
test('calls attachTo() on components registered for identifier on nodes w/ data-mdc-auto-init attr', () => {
@@ -57,6 +69,11 @@ test('calls attachTo() on components registered for identifier on nodes w/ data-
5769
assert.isOk(root.querySelector('.mdc-fake').FakeComponent instanceof FakeComponent);
5870
});
5971

72+
test('throws when attachTo() is missing', () => {
73+
const root = setupInvalidTest();
74+
assert.throws(() => mdcAutoInit(root));
75+
});
76+
6077
test('passes the node where "data-mdc-auto-init" was found to attachTo()', () => {
6178
const root = setupTest();
6279
mdcAutoInit(root);

0 commit comments

Comments
 (0)