From 51320204f9a7bb58551ead98b6c8946e3d93fb21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Sat, 4 May 2019 22:26:51 +0200 Subject: [PATCH] [IMP] component: add static css support for sub components part of issue #80 --- doc/component.md | 37 +++++++++++++++++++++++++++++-------- src/qweb.ts | 15 ++++++++++++++- tests/component.test.ts | 31 +++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 9 deletions(-) diff --git a/doc/component.md b/doc/component.md index cba5b9c4..caf29ce2 100644 --- a/doc/component.md +++ b/doc/component.md @@ -4,7 +4,7 @@ - [Overview](#overview) - [Example](#example) -- [Templates](#templates) +- [Composition](#composition) - [Reference](#reference) - [Properties](#properties) - [Methods](#methods) @@ -65,7 +65,7 @@ a state object is defined. It is not mandatory to use the state object, but it is certainly encouraged. The state object is [observed](observer.md), and any change to it will cause a rerendering. -## Templates +## Composition The example above shows a QWeb template with a `t-on-click` directive. Widget templates are standard [QWeb](qweb.md) templates, but with an extra directive: @@ -73,18 +73,39 @@ templates are standard [QWeb](qweb.md) templates, but with an extra directive: widgets: ```xml -
- some text - -
+
+ some text + +
``` -In this example, the template create a widget MyWidget just after the span. See -the [QWeb](qweb.md) documentation for more information on the `t-widget` directive. +```js +class ParentWidget extends owl.Component { + template = 'parent'; + widgets = { MyWidget: MyWidget}; + ... +} +``` + +In this example, the `ParentWidget`'s template creates a widget `MyWidget` just +after the span. See the [QWeb](qweb.md) documentation for more information on the +`t-widget` directive. Note that the rendering context for the template is the widget itself. This means that the template can access `state`, `props`, `env`, or any methods defined in the widget. +**CSS and style:** there is some specific support to allow the parent to declare +additional css classes or style for the sub widget: css declared in `class`, `style`, `t-att-class` or `t-att-style` will be added to the +root widget element. + +```xml +
+ +
+``` + + + ## Reference An Owl component is a small class which represent a widget or some UI element. diff --git a/src/qweb.ts b/src/qweb.ts index 8798e146..4b5b3c71 100644 --- a/src/qweb.ts +++ b/src/qweb.ts @@ -1212,8 +1212,21 @@ const widgetDirective: Directive = { finalizeWidgetCode += `;delete context.refs[${refKey}]`; } ctx.addIf(`isNew${widgetID}`); + let createHook = ""; + let classAttr = node.getAttribute("class"); + let styleAttr = node.getAttribute("style"); + if (classAttr || styleAttr) { + const classCode = classAttr + ? classAttr + .split(" ") + .map(c => `vn.elm.classList.add('${c}')`) + .join(";") + ";" + : ""; + const styleCode = styleAttr ? `vn.elm.style = '${styleAttr}'` : ""; + createHook = `vnode.data.hook = {create(_, vn){${classCode}${styleCode}}};`; + } ctx.addLine( - `def${defID} = def${defID}.then(vnode=>{let pvnode=h(vnode.sel, {key: ${templateID}});c${ + `def${defID} = def${defID}.then(vnode=>{${createHook}let pvnode=h(vnode.sel, {key: ${templateID}});c${ ctx.parentNode }[_${dummyID}_index]=pvnode;pvnode.data.hook = {insert(vn){let nvn=w${widgetID}._mount(vnode, vn.elm);pvnode.elm=nvn.elm;${refExpr}},remove(){${finalizeWidgetCode}},destroy(){${finalizeWidgetCode}}}; w${widgetID}.__owl__.pvnode = pvnode;});` ); diff --git a/tests/component.test.ts b/tests/component.test.ts index ce9ef951..0af094cc 100644 --- a/tests/component.test.ts +++ b/tests/component.test.ts @@ -1224,6 +1224,37 @@ describe("props evaluation (with t-props directive)", () => { }); }); +describe("class and style attributes with t-widget", () => { + test("class is properly added on widget root el", async () => { + class ParentWidget extends Widget { + inlineTemplate = ` +
+ +
`; + widgets = { child: Child }; + } + class Child extends Widget { + inlineTemplate = `
` + } + const widget = new ParentWidget(env); + await widget.mount(fixture); + expect(fixture.innerHTML).toBe(`
`); + }); + + test("style is properly added on widget root el", async () => { + class ParentWidget extends Widget { + inlineTemplate = ` +
+ +
`; + widgets = { child: Widget }; + } + const widget = new ParentWidget(env); + await widget.mount(fixture); + expect(fixture.innerHTML).toBe(`
`); + }); +}); + describe("other directives with t-widget", () => { test("t-on works as expected", async () => { let n = 0;