UNPKG

app-decorators

Version:

Collection of useful ES7 Decorators, writtin in ES6, that can be used for building webapps

743 lines (609 loc) 85.7 kB
System.register(['app-decorators/src/libs/random-storage', 'app-decorators/src/libs/customelement', 'app-decorators/src/libs/element-to-function', 'jquery', 'underscore', '../../src/bootstrap', '../../src/helpers/delay', '../../src/libs/random-storage', '../../src/helpers/jquery.click-and-wait', 'sinon'], function (_export, _context) { "use strict"; var _storage, _Register, _elementToFunc, $, _, bootstrapPolyfills, delay, storage, sinon, _createClass, _this2; function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { var desc = {}; Object['ke' + 'ys'](descriptor).forEach(function (key) { desc[key] = descriptor[key]; }); desc.enumerable = !!desc.enumerable; desc.configurable = !!desc.configurable; if ('value' in desc || desc.initializer) { desc.writable = true; } desc = decorators.slice().reverse().reduce(function (desc, decorator) { return decorator(target, property, desc) || desc; }, desc); if (context && desc.initializer !== void 0) { desc.value = desc.initializer ? desc.initializer.call(context) : void 0; desc.initializer = undefined; } if (desc.initializer === void 0) { Object['define' + 'Property'](target, property, desc); desc = null; } return desc; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } return { setters: [function (_appDecoratorsSrcLibsRandomStorage) { _storage = _appDecoratorsSrcLibsRandomStorage; }, function (_appDecoratorsSrcLibsCustomelement) { _Register = _appDecoratorsSrcLibsCustomelement; }, function (_appDecoratorsSrcLibsElementToFunction) { _elementToFunc = _appDecoratorsSrcLibsElementToFunction.default; }, function (_jquery) { $ = _jquery.default; }, function (_underscore) { _ = _underscore.default; }, function (_srcBootstrap) { bootstrapPolyfills = _srcBootstrap.bootstrapPolyfills; }, function (_srcHelpersDelay) { delay = _srcHelpersDelay.delay; }, function (_srcLibsRandomStorage) { storage = _srcLibsRandomStorage.storage; }, function (_srcHelpersJqueryClickAndWait) {}, function (_sinon) { sinon = _sinon.default; }], execute: function () { _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); _this2 = this; // init special innerHTML for test String.prototype.cs = function () { return this.replace(/[\t\n\r ]+/gm, ''); }; String.prototype.nlte = function () { return this.replace(/[\t\r\n ]+/g, '').trim(); }; describe('@style decorator', _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee7() { var _ref2, component, view, style, on; return regeneratorRuntime.wrap(function _callee7$(_context8) { while (1) { switch (_context8.prev = _context8.next) { case 0: _context8.next = 2; return bootstrapPolyfills; case 2: _context8.next = 4; return System.import('app-decorators'); case 4: _ref2 = _context8.sent; component = _ref2.component; view = _ref2.view; style = _ref2.style; on = _ref2.on; beforeEach(function () { return $('body').append('<div id="test-style-order"></div>'); }); afterEach(function () { return $('#test-style-order').remove(); }); it('should call customElements hooks in right order', _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() { var _dec, _dec2, _dec3, _class, _class2, _temp; var Style, createdCallback, attachedCallback, detachedCallback, spy_style_created, spy_style_attached, spy_style_detached, styleOrderCom; return regeneratorRuntime.wrap(function _callee$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: Style = (_dec = style([{ attachOn: "immediately", imports: [], styles: 'style-order .foo { \n color: gray \n } \n', type: "default" }]), _dec2 = view('\n <div class="foo">Hello World</div>\n '), _dec3 = component({ name: 'style-order' }), _dec(_class = _dec2(_class = _dec3(_class = (_temp = _class2 = function (_elementToFunc2) { _inherits(Style, _elementToFunc2); function Style() { _classCallCheck(this, Style); return _possibleConstructorReturn(this, (Style.__proto__ || Object.getPrototypeOf(Style)).apply(this, arguments)); } return Style; }(_elementToFunc(HTMLDivElement)), _class2.$$componentName = 'Style', _temp)) || _class) || _class) || _class); /** * Setup */ _Register.Register.customElement(Style, _storage.storage.get(Style).get('@component')); createdCallback = storage.get(Style).get('@callbacks').get('created'); attachedCallback = storage.get(Style).get('@callbacks').get('attached'); detachedCallback = storage.get(Style).get('@callbacks').get('detached'); spy_style_created = sinon.spy(createdCallback, 0); spy_style_attached = sinon.spy(attachedCallback, 0); spy_style_detached = sinon.spy(detachedCallback, 0); styleOrderCom = Style.create(); $('#test-style-order').append(styleOrderCom); $('#test-style-order style-order').remove(); $('#test-style-order').append(styleOrderCom); _context2.next = 14; return delay(1); case 14: /** * Test */ spy_style_created.callCount.should.be.equal(1); spy_style_attached.callCount.should.be.equal(2); spy_style_detached.callCount.should.be.equal(1); // cleanup createdCallback[0].restore(); attachedCallback[0].restore(); detachedCallback[0].restore(); case 20: case 'end': return _context2.stop(); } } }, _callee, _this2); }))); it('should contain stylesheets in element', function () { var _dec4, _dec5, _dec6, _class3, _class4, _temp2; var Style = (_dec4 = style([{ attachOn: "immediately", imports: [], styles: 'style-collection-string {\n width: 100px;\n height: 100px;\n } \n.foo {\n color: red;\n } \n', type: "default" }, { attachOn: "load", imports: ["http://localhost:4000/styles/test-1.css", "http://localhost:4000/styles/test-2.css"], styles: "", type: "on" }]), _dec5 = view('\n <div class="foo">Hello World</div>\n '), _dec6 = component({ name: 'style-collection-string' }), _dec4(_class3 = _dec5(_class3 = _dec6(_class3 = (_temp2 = _class4 = function (_elementToFunc3) { _inherits(Style, _elementToFunc3); function Style() { _classCallCheck(this, Style); return _possibleConstructorReturn(this, (Style.__proto__ || Object.getPrototypeOf(Style)).apply(this, arguments)); } return Style; }(_elementToFunc(HTMLDivElement)), _class4.$$componentName = 'Style', _temp2)) || _class3) || _class3) || _class3); _Register.Register.customElement(Style, _storage.storage.get(Style).get('@component')); var element = Style.create(); // should have correct length element.$stylesheets.should.be.have.length(2); // should have correct properties (0) element.$stylesheets[0]._styles = element.$stylesheets[0]._styles.nlte(); element.$stylesheets[0]._attachOn.should.equal("immediately"); element.$stylesheets[0]._imports.should.containDeep([]); element.$stylesheets[0]._styles.should.equal("style-collection-string{width:100px;height:100px;}" + ".foo{color:red;}"); element.$stylesheets[0]._type.should.equal("default"); // should have correct properties (1) element.$stylesheets[1]._attachOn.should.equal("load"); element.$stylesheets[1]._imports.should.containDeep(["http://localhost:4000/styles/test-1.css", "http://localhost:4000/styles/test-2.css"]); element.$stylesheets[1]._styles.should.equal(""); element.$stylesheets[1]._type.should.equal("on"); }); it('should contain stylesheets once stylesheet when external resources', function () { var _dec7, _dec8, _dec9, _class5, _class6, _temp3; var Style = (_dec7 = style([{ attachOn: "immediately", imports: [], styles: 'style-collection-string {\n width: 100px;\n height: 100px;\n } \n.foo {\n color: red;\n } \n', type: "default" }, { attachOn: "load", imports: ["http://localhost:4000/styles/test-1.css", "http://localhost:4000/styles/test-2.css"], styles: "", type: "on" }]), _dec8 = view('\n <div class="foo">Hello World</div>\n '), _dec9 = component({ name: 'style-sheet-once' }), _dec7(_class5 = _dec8(_class5 = _dec9(_class5 = (_temp3 = _class6 = function (_elementToFunc4) { _inherits(Style, _elementToFunc4); function Style() { _classCallCheck(this, Style); return _possibleConstructorReturn(this, (Style.__proto__ || Object.getPrototypeOf(Style)).apply(this, arguments)); } return Style; }(_elementToFunc(HTMLDivElement)), _class6.$$componentName = 'Style', _temp3)) || _class5) || _class5) || _class5); _Register.Register.customElement(Style, _storage.storage.get(Style).get('@component')); var element = Style.create(); var element2 = Style.create(); // shoul create only once stylesheet per component should(element.querySelectorAll('link').length).be.equal(2); should(element2.querySelectorAll('link').length).be.equal(0); }); it('should render external resources (its failed when not comes quickly). So thats normal. Try again', _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() { var _dec10, _dec11, _dec12, _dec13, _class7, _desc, _value, _class8, _class9, _temp4; var Style, element, stylesheet, href1, href2, expected; return regeneratorRuntime.wrap(function _callee2$(_context3) { while (1) { switch (_context3.prev = _context3.next) { case 0: Style = (_dec10 = style([{ attachOn: "immediately", imports: [], styles: 'style-collection-string {\n width: 100px;\n height: 100px;\n } \n.foo {\n color: red;\n } \n', type: "default" }, { attachOn: "load", imports: ["http://localhost:4000/styles/test-1.css", "http://localhost:4000/styles/test-2.css"], styles: "", type: "on" }]), _dec11 = view('\n <div class="foo">Hello World</div>\n '), _dec12 = component({ name: 'style-external-resources' }), _dec13 = on('load:stylesheet'), _dec10(_class7 = _dec11(_class7 = _dec12(_class7 = (_class8 = (_temp4 = _class9 = function (_elementToFunc5) { _inherits(Style, _elementToFunc5); function Style() { _classCallCheck(this, Style); return _possibleConstructorReturn(this, (Style.__proto__ || Object.getPrototypeOf(Style)).apply(this, arguments)); } _createClass(Style, [{ key: 'onLoadStylesheet', value: function onLoadStylesheet(e) { //console.log('load:stylesheet'); } }]); return Style; }(_elementToFunc(HTMLDivElement)), _class9.$$componentName = 'Style', _temp4), (_applyDecoratedDescriptor(_class8.prototype, 'onLoadStylesheet', [_dec13], Object.getOwnPropertyDescriptor(_class8.prototype, 'onLoadStylesheet'), _class8.prototype)), _class8)) || _class7) || _class7) || _class7); _Register.Register.customElement(Style, _storage.storage.get(Style).get('@component')); element = Style.create(); stylesheet = element.$stylesheets[0]; href1 = "http://localhost:4000/styles/test-1.css"; href2 = "http://localhost:4000/styles/test-2.css"; expected = '\n <style-external-resources rendered="true">\n <style class="style-order-0">\n style-collection-string { \n width: 100px; \n height: 100px; \n } \n .foo { \n color: red; \n }\n </style>\n {{%LINK%}}\n <div class="foo">Hello World</div>\n </style-external-resources>'.nlte(); document.body.appendChild(element); _context3.next = 10; return delay(500); case 10: if (stylesheet.supportRelPreload()) { //@TODO: remove inside of onload to helper functions expected = expected.replace('{{%LINK%}}', '\n <link rel="stylesheet" as="style" href="' + href1 + '" onload="this.rel=\'stylesheet\'; this.__event = new CustomEvent(\'load:stylesheet\', { bubbles: true });this.dispatchEvent(this.__event)" class="style-order-1">\n <link rel="stylesheet" as="style" href="' + href2 + '" onload="this.rel=\'stylesheet\'; this.__event = new CustomEvent(\'load:stylesheet\', { bubbles: true });this.dispatchEvent(this.__event)" class="style-order-1">').nlte(); element.outerHTML.nlte().should.be.equal(expected); } else { expected = expected.replace('{{%LINK%}}', '\n <link rel="stylesheet" href="' + href1 + '" media="all" class="style-order-1">\n <link rel="stylesheet" href="' + href2 + '" media="all" class="style-order-1">').nlte(); element.outerHTML.nlte().should.be.equal(expected); } case 11: case 'end': return _context3.stop(); } } }, _callee2, _this2); }))); /** it.skip('should show proposals for conditions with atrule events', () => { @style(` // existing features @on load { @fetch http://localhost:4000/styles/test-1.css; } @on click .foo { @fetch http://localhost:4000/styles/test-2.css; } @action /a/b/{{value}}.html{ @fetch http://localhost:4000/styles/test-3.css; } style-collection-string { width: 100px; height: 100px; } .foo { color: red; } // proposal input: @if {{shouldAttributeName}} { // how easy its to write a postcss tokenizer, parser plugin for this @if rule ( question to owner?) .bar { font-size: var(--some-box-size-a); color: {{shouldAttributeName2}} } @fetch http://localhost:4000/styles/test-2.css; } // proposal out: // ## lifecycle: only one call: when destroyed event triggered or // when "shouldAttributeName" contain correct value // that should be tested (no tested)! and export function cleanup(event){ // required for nested components if(rootNode !== event.currentTarget) { return; } observer && observer.disconnect && observer.disconnect(); } export function condition({ shouldAttributeName, rootDomNode, settings, handler, type = "if" }){ // type = or "unless" // create an observer instance let observer = new MutationObserver(mutations => { for(let mutation of mutations) { if(type === 'unless' && !shouldAttributeName) { createDisconnectHandler({ observer, rootDomNode, settings}); return; } let mutationTarget = mutation.target; let attrValue = mutationTarget.getAttribute(attributeName); // shouldAttributeName must be if(mutation.attributeName; !== shouldAttributeName){ return; } // // example settings: see: /test/libs/stylesheet.spec.js // let settings = { // attachOn: 'immediately', // type: 'default', // imports: [ // "http://localhost:4000/styles/test-2.css" // ], // styles: '.bar {font-size: var(--some-box-size-a)}' // }, // handler({ ... }) === new Stylesheet({ ... }); see: /test/libs/stylesheet.spec.js cleanup(true) } }); // configuration of the observer: var config = { attributes: true, characterData: true }; // pass in the target node, as well as the observer options observer.observe(rootNode, config); // Cleanup // stop observing stylesheet:attached event triggered rootNode.addEventListener('stylesheet:attached', event => cleanup(event)); // stop observing when component detached event triggered rootNode.addEventListener('detached', event => cleanup(event)); } // ####################################### // ####################################### @unless {{foo}} { .bar { font-size: var(--some-box-size-b); color: red; } } @each {{values}} { .baz-{{size}} { background-color: var(--this--color); } } `) @view(` <div class="foo">Hello World</div> <a class="bar" href="/a/b/c.html">Huhuu</a> <a class="baz" href="/a/b/d.html">Huhuu</a> `) @component({ name: 'com-other-atrules', }) class Style { // ensure "colors" variable inside @style.bin colors = null; @on('click .foo') onClickFoo(e){ } @on('load:stylesheet') onLoadStylesheet(e){ } } }); **/ it('should create only once stylesheets no matter how often triggered (on, action)', _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3() { var _dec14, _dec15, _dec16, _dec17, _dec18, _class10, _desc2, _value2, _class11, _class12, _temp5; var Style, element; return regeneratorRuntime.wrap(function _callee3$(_context4) { while (1) { switch (_context4.prev = _context4.next) { case 0: Style = (_dec14 = style([{ attachOn: "immediately", imports: [], styles: 'style-collection-string {\n width: 100px;\n height: 100px;\n } \n.foo {\n color: red;\n } \n', type: "default" }, { attachOn: "load", imports: ["http://localhost:4000/styles/test-1.css"], styles: "", type: "on" }, { attachOn: "click .foo", imports: ["http://localhost:4000/styles/test-2.css"], styles: "", type: "on" }, { attachOn: "/a/b/{{value}}.html /a/b/{{value}}.html", imports: ["http://localhost:4000/styles/test-3.css"], styles: "", type: "action" }]), _dec15 = view('\n <div class="foo">Hello World</div>\n <a class="bar" href="/a/b/c.html">Huhuu</a>\n <a class="baz" href="/a/b/d.html">Huhuu</a>\n '), _dec16 = component({ name: 'com-style-action' }), _dec17 = on('click .foo'), _dec18 = on('load:stylesheet'), _dec14(_class10 = _dec15(_class10 = _dec16(_class10 = (_class11 = (_temp5 = _class12 = function (_elementToFunc6) { _inherits(Style, _elementToFunc6); function Style() { _classCallCheck(this, Style); return _possibleConstructorReturn(this, (Style.__proto__ || Object.getPrototypeOf(Style)).apply(this, arguments)); } _createClass(Style, [{ key: 'onClickFoo', value: function onClickFoo(e) {} }, { key: 'onLoadStylesheet', value: function onLoadStylesheet(e) {} }]); return Style; }(_elementToFunc(HTMLDivElement)), _class12.$$componentName = 'Style', _temp5), (_applyDecoratedDescriptor(_class11.prototype, 'onClickFoo', [_dec17], Object.getOwnPropertyDescriptor(_class11.prototype, 'onClickFoo'), _class11.prototype), _applyDecoratedDescriptor(_class11.prototype, 'onLoadStylesheet', [_dec18], Object.getOwnPropertyDescriptor(_class11.prototype, 'onLoadStylesheet'), _class11.prototype)), _class11)) || _class10) || _class10) || _class10); // Style.stub('onClickFoo', () => console.log('stubed')); _Register.Register.customElement(Style, _storage.storage.get(Style).get('@component')); element = Style.create(); document.body.appendChild(element); // round 1 $('.foo', element).clickAndWait(30); $('.bar', element).clickAndWait(30); $('.baz', element).clickAndWait(30); // round 2 $('.foo', element).clickAndWait(30); $('.bar', element).clickAndWait(30); $('.baz', element).clickAndWait(30); // round 3 $('.foo', element).clickAndWait(30); $('.bar', element).clickAndWait(30); $('.baz', element).clickAndWait(30); _context4.next = 15; return delay(50); case 15: should(element.querySelectorAll('style').length).be.equal(1); should(element.querySelectorAll('link').length).be.equal(3); case 17: case 'end': return _context4.stop(); } } }, _callee3, _this2); }))); it('should create element 1,2,3 and then remove 1,2,3 + custom event that transfer event status to the next node', _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee4() { var _dec19, _dec20, _dec21, _class13, _class14, _temp6; var Style, element1, fooEvent, element2, element3, element4; return regeneratorRuntime.wrap(function _callee4$(_context5) { while (1) { switch (_context5.prev = _context5.next) { case 0: Style = (_dec19 = style([{ attachOn: "immediately", imports: [], styles: '.foo { color: red } \n', type: "default" }, { attachOn: "foo", imports: ["http://localhost:4000/styles/test-1.css", "http://localhost:4000/styles/test-2.css"], styles: "", type: "on" }]), _dec20 = view('<div class="foo">Hello World</div>'), _dec21 = component({ name: 'com-multiple-appends' }), _dec19(_class13 = _dec20(_class13 = _dec21(_class13 = (_temp6 = _class14 = function (_elementToFunc7) { _inherits(Style, _elementToFunc7); function Style() { _classCallCheck(this, Style); return _possibleConstructorReturn(this, (Style.__proto__ || Object.getPrototypeOf(Style)).apply(this, arguments)); } return Style; }(_elementToFunc(HTMLDivElement)), _class14.$$componentName = 'Style', _temp6)) || _class13) || _class13) || _class13); _Register.Register.customElement(Style, _storage.storage.get(Style).get('@component')); /** * Test createdCount when its incremented */ // first created element (element1) take the role of style and link element1 = Style.create(); document.body.appendChild(element1); fooEvent = new Event('foo'); element1.dispatchEvent(fooEvent); _context5.next = 8; return delay(20); case 8: should(storage.get(Style).get('@style').get('referenceNodes').size).be.equal(1); should(element1.querySelectorAll('style').length).be.equal(1); should(element1.querySelectorAll('link').length).be.equal(2); // create element2 element2 = Style.create(); document.body.appendChild(element2); should(storage.get(Style).get('@style').get('referenceNodes').size).have.equal(2); should(element2.querySelectorAll('style').length).be.equal(0); should(element2.querySelectorAll('link').length).be.equal(0); // create element3 element3 = Style.create(); document.body.appendChild(element3); should(storage.get(Style).get('@style').get('referenceNodes').size).have.equal(3); should(element3.querySelectorAll('style').length).be.equal(0); should(element3.querySelectorAll('link').length).be.equal(0); // remove element1, so they lost there style and link $(element1).remove(); _context5.next = 24; return delay(20); case 24: should(storage.get(Style).get('@style').get('referenceNodes').size).have.equal(2); should(element1.querySelectorAll('style').length).be.equal(0); should(element1.querySelectorAll('link').length).be.equal(0); // element2, takes the role of styles, see style and link should(storage.get(Style).get('@style').get('referenceNodes').size).have.equal(2); should(element2.querySelectorAll('style').length).be.equal(1); should(element2.querySelectorAll('link').length).be.equal(2); // element3, nothing change should(element3.querySelectorAll('style').length).be.equal(0); should(element3.querySelectorAll('link').length).be.equal(0); // remove element2, so they lost there style and link $(element2).remove(); _context5.next = 35; return delay(20); case 35: should(storage.get(Style).get('@style').get('referenceNodes').size).have.equal(1); should(element2.querySelectorAll('style').length).be.equal(0); should(element2.querySelectorAll('link').length).be.equal(0); // element3, takes the role of styles, see style and link should(element3.querySelectorAll('style').length).be.equal(1); should(element3.querySelectorAll('link').length).be.equal(2); // remove element2, so they lost there style and link $(element3).remove(); _context5.next = 43; return delay(20); case 43: should(storage.get(Style).get('@style').get('referenceNodes').size).have.equal(0); should(element3.querySelectorAll('style').length).be.equal(0); should(element3.querySelectorAll('link').length).be.equal(0); // append element1 again to dom, they should take the role of style and link document.body.appendChild(element1); _context5.next = 49; return delay(20); case 49: should(storage.get(Style).get('@style').get('referenceNodes').size).have.equal(1); should(element1.querySelectorAll('style').length).be.equal(1); should(element1.querySelectorAll('link').length).be.equal(2); // append element2 again to dom, they doesnt takes the role of style and link document.body.appendChild(element2); _context5.next = 55; return delay(20); case 55: should(storage.get(Style).get('@style').get('referenceNodes').size).have.equal(2); // element1 should(element1.querySelectorAll('style').length).be.equal(1); should(eleme