Skip to content

Commit

Permalink
Merge pull request #87 from canjs/major
Browse files Browse the repository at this point in the history
Merge major branch for 6.0 release
  • Loading branch information
matthewp authored Oct 2, 2019
2 parents b3d6ffb + 6f3a2ce commit 718505a
Show file tree
Hide file tree
Showing 23 changed files with 874 additions and 216 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ indent_size = 4
trim_trailing_whitespace = false
insert_final_newline = true

[{*.json,*.yml}]
[{*.json,*.yml,*.md}]
indent_style = space
indent_size = 2
78 changes: 78 additions & 0 deletions docs/bindings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
@function can-stache-element/lifecycle-methods.bindings bindings
@parent can-stache-element/lifecycle-methods 6

Programmatically instantiate a component

@signature `element.bindings([props])`

Create bindings between an element's properties and parent observables. This is useful when you:

- have complex logic for switching between different components (e.g. routing)

The following defines `HomePage` and `MyApp` components that sets up bindings between them:

```js
class HomePage extends StacheElement {
static props = {
pageId: Number
}
}

class MyApp extends StacheElement {
static view = `{{{component}}}`

static props = {
componentName: String,
pageId: Number,

component: {
get() {
if(componentName === "home") {
return new HomePage().bindings({
pageId: value.from(this, "pageId")
});
}
}
}
}
}
```

@param {Object} [props] Child properties to bind to.
@return {can-stache-element} The StacheElement instance.

@body

## Use

Pass an object of properties to bind to the element. For example:

```js
import {StacheElement, ObservableObject, value} from "can";

const appVM = new ObservableObject({
association: "friend"
});

class MyGreeting extends StacheElement {
static view = `{{greeting}} {{subject}}`

static props = {
greeting: String,
subject: String
}
}
customElements.define("my-greeting", MyGreeting);

const myGreetingInstance = new MyGreeting().bindings({
greeting: "Hello",
subject: value.bind(appVM, "association")
}).render();

console.log( myGreetingInstance );
// logs <my-greeting>Hello friend</my-greeting>

console.log( myGreetingInstance.subject );
// logs "friend"
```
@codepen
2 changes: 1 addition & 1 deletion docs/can-stache-element.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@module {function} can-stache-element
@parent can-views
@collection can-ecosystem
@collection can-core
@group can-stache-element/static 0 static
@group can-stache-element/lifecycle-methods 1 lifecycle methods
@group can-stache-element/lifecycle-hooks 2 lifecycle hooks
Expand Down
14 changes: 8 additions & 6 deletions docs/props.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,25 @@
```
@codepen

> Note: to see all the options supported by `define`, see [can-observable-object].
> Note: to see all the options supported by `props`, see [can-observable-object].
@signature `static get props() { return { ... }; }`

For browsers that do not support class fields (and applications not using a transpiler), properties can be defined using a `static` getter like shown below.

```js
class TodoItem extends StacheElement {
static props = {
name: String,
completed: false
};
static get props() {
return {
name: String,
completed: false
};
}
}
customElements.define("todo-item", TodoItem);
```

> Note: to see all the options supported by `define`, see [can-observable-object].
> Note: to see all the options supported by `props`, see [can-observable-object].
@body

Expand Down
26 changes: 15 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "can-stache-element",
"description": "Create custom elements with can-stache, can-observable-object, and JavaScript classes",
"version": "0.8.1",
"version": "1.0.0-pre.16",
"author": {
"name": "Bitovi",
"email": "[email protected]",
Expand All @@ -12,26 +12,29 @@
"url": "https://github.com/canjs/can-stache-element/issues"
},
"dependencies": {
"can-bind": "^1.4.3",
"can-child-nodes": "^1.2.1",
"can-bind": "^1.5.0",
"can-define-lazy-value": "^1.1.0",
"can-dom-mutate": "^2.0.3",
"can-log": "^1.0.2",
"can-observable-mixin": "<2.0.0",
"can-observable-mixin": "^1.0.0-pre.11",
"can-queues": "^1.3.0",
"can-simple-observable": "^2.4.2",
"can-stache": "^4.0.0",
"can-stache-bindings": "^4.0.0",
"can-symbol": "^1.6.4",
"can-view-nodelist": "^4.3.4"
"can-stache": "^5.0.0-pre.2",
"can-stache-bindings": "^5.0.0-pre.6",
"can-symbol": "^1.6.4"
},
"devDependencies": {
"@babel/cli": "^7.4.4",
"@babel/core": "^7.4.5",
"@babel/preset-env": "^7.4.5",
"@webcomponents/custom-elements": "^1.2.4",
"can-observable-bindings": "^1.1.0",
"can-reflect": "^1.17.11",
"can-reflect-dependencies": "^1.1.2",
"can-test-helpers": "^1.1.4",
"can-type": "^0.2.0",
"can-type": "^1.0.0-pre.2",
"can-value": "^1.1.1",
"can-view-callbacks": "^4.4.0",
"can-view-callbacks": "^5.0.0-pre.1",
"can-view-scope": "^4.13.1",
"detect-cyclic-packages": "^1.1.0",
"fixpack": "^2.3.1",
Expand All @@ -53,13 +56,14 @@
"url": "git://github.com/canjs/can-stache-element.git"
},
"scripts": {
"ci": "npm run test && node test/test-saucelabs.js && npm run compile-to-es5",
"ci": "npm run test && node test/test-saucelabs.js",
"compile-to-es5": "babel src --out-dir dist",
"detect-cycle": "detect-cyclic-packages",
"http-server": "http-server -p 3000 --silent",
"jshint": "jshint src/*.js test/*.js --config",
"lint": "fixpack && npm run jshint",
"postversion": "git push --follow-tags",
"prepublishOnly": "npm run compile-to-es5",
"preversion": "npm test",
"test": "npm run detect-cycle && npm run lint && npm run testee",
"testee": "testee test/test.html --browsers firefox"
Expand Down
153 changes: 151 additions & 2 deletions src/can-stache-element-test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
const QUnit = require("steal-qunit");
const Scope = require("can-view-scope");
const viewCallbacks = require("can-view-callbacks");
const stacheBindings = require("can-stache-bindings");
const stache = require("can-stache");
const SimpleObservable = require("can-simple-observable");
const StacheElement = require("./can-stache-element");
const browserSupports = require("../test/browser-supports");
const type = require("can-type");
const browserSupports = require("../test/helpers").browserSupports;
const canReflect = require("can-reflect");
const dev = require("can-test-helpers").dev;

Expand All @@ -16,7 +18,7 @@ if (browserSupports.customElements) {

class Input extends StacheElement {
static get view() {
return `<p><input value:bind="this.inputValue" on:change="this.handleChange(scope.element.value)"></p>`;
return `<p><input value:from="this.inputValue" on:change="this.handleChange(scope.element.value)"></p>`;
}

handleChange(val) {
Expand Down Expand Up @@ -291,4 +293,151 @@ if (browserSupports.customElements) {
assert.equal(undo(), 1, "Warned for the 'click' prop");
});

QUnit.test("bindings run once (#72)", function(assert) {

class CreateBindingsOnce extends StacheElement {
static get props(){
return {
value: Number
};
}
}

customElements.define('create-bindings-once', CreateBindingsOnce);

var calls = 0;

stache("<create-bindings-once value:from='this.read()'/>")({
read(){
calls++;
return 1;
}
});

assert.equal(calls,1, "only called once");
});

QUnit.test("initializeViewModel called once for elements rendered with stache", function(assert) {
const origInitializeViewModel = stacheBindings.behaviors.initializeViewModel;
let calls = 0;
stacheBindings.behaviors.initializeViewModel = function() {
calls++;
return origInitializeViewModel.apply(this, arguments);
};

class InitializeViewModelOnce extends StacheElement {
static get props(){
return {
num: type.convert(Number)
};
}
}
customElements.define('initialize-viewmodel-once', InitializeViewModelOnce);

const frag = stache("<initialize-viewmodel-once />")({});

document.querySelector("#qunit-fixture").appendChild(frag);

assert.equal(calls, 1, "only called once");
});

QUnit.test("initializeViewModel not called if there are no bindings", function(assert) {
const origInitializeViewModel = stacheBindings.behaviors.initializeViewModel;
let calls = 0;
stacheBindings.behaviors.initializeViewModel = function() {
calls++;
return origInitializeViewModel.apply(this, arguments);
};

class InitializeViewModelZeroTimes extends StacheElement {
static get props(){
return {
num: type.convert(Number)
};
}
}
customElements.define('initialize-viewmodel-zero-times', InitializeViewModelZeroTimes);

new InitializeViewModelZeroTimes()
.initialize();

assert.equal(calls, 0, "initializeViewModel not called");
});

QUnit.test("can-template support (#77)",function(assert){

class CanGetTemplates extends StacheElement {
static get view(){
return stache("can-get-templates.stache",
"{{this.foo( passed='PASSED' )}}");
}
static get props(){
return {inner: "INNER"};
}
}

customElements.define('can-get-templates', CanGetTemplates);

var template = stache("outer.stache",
"{{let letScope='LETSCOPE'}}"+
"<can-get-templates>"+
"<can-template name='foo'>"+
"<div class='outer'>{{this.outer}}</div>"+
"<div class='let-scope'>{{letScope}}</div>"+
"<div class='passed'>{{passed}}</div>"+
"</can-template>"+
"</can-get-templates>");

var frag = template({
outer: "OUTER"
});

assert.equal(frag.firstElementChild.querySelector(".outer").innerHTML, "OUTER", "Access OUTER scope");

assert.equal(frag.firstElementChild.querySelector(".let-scope").innerHTML, "LETSCOPE", "Access let scope scope");

assert.equal(frag.firstElementChild.querySelector(".passed").innerHTML, "PASSED", "Access passed scope");

});

QUnit.test("can-template called outside stache works (#77)",function(assert){
class CanGetTemplatesInCode extends StacheElement {
static get view() {
return `{{ this.bar() }}`;
}
static get props() {
return {
inner: "INNER"
};
}

bar() {
return this.foo({ passed: "PASSED" });
}
}
customElements.define("can-get-templates-in-code", CanGetTemplatesInCode);

var template = stache("outer.stache",
`{{ let letScope="LETSCOPE" }}
<can-get-templates-in-code>
<can-template name="foo">
<div class="outer">{{ this.outer }}</div>
<div class="let-scope">{{ letScope }}</div>
<div class="passed">{{ passed }}</div>
</can-template>
</can-get-templates-in-code>`);

var frag = template({
outer: "OUTER"
});

assert.equal(frag.firstElementChild.querySelector(".outer").innerHTML, "OUTER", "Access OUTER scope");

assert.equal(frag.firstElementChild.querySelector(".let-scope").innerHTML, "LETSCOPE", "Access let scope scope");

assert.equal(frag.firstElementChild.querySelector(".passed").innerHTML, "PASSED", "Access passed scope");


});

}
Loading

0 comments on commit 718505a

Please sign in to comment.