[IMP] tags: reintroduce inline css tag

The CSS tag is useful to define a css stylesheet in the javascript file:
```js
class MyComponent extends Component {
  static template = xml`
        <div class="my-component">some template</div>
    `;
  static style = css`
    .my-component {
      color: red;
    }
  `;
}
```

The `css` tag registers internally the css information. Then, whenever the first instance of the component is created, will add a <style> tag to the document <head>.

Original commit in Owl v1: 953778dc5
This commit is contained in:
Bruno Boi
2021-11-03 10:00:15 +01:00
committed by Géry Debongnie
parent 4e4b85e2a9
commit ee5ad354ff
6 changed files with 266 additions and 143 deletions
+158 -142
View File
@@ -1,159 +1,175 @@
// import { Component, Env } from "../../src/component/component";
// import { processSheet } from "../../src/component/styles";
// import { xml, css } from "../../src/tags";
// import { makeTestFixture, makeTestEnv } from "../helpers";
import { Component, css, mount, xml } from "../../src";
import { makeTestFixture } from "../helpers";
// //------------------------------------------------------------------------------
// // Setup and helpers
// //------------------------------------------------------------------------------
let fixture: HTMLElement;
beforeEach(() => {
fixture = makeTestFixture();
document.head.innerHTML = "";
});
// // We create before each test:
// // - fixture: a div, appended to the DOM, intended to be the target of dom
// // manipulations. Note that it is removed after each test.
// // - env: an Env, necessary to create new components
// let fixture: HTMLElement;
// let env: Env;
// beforeEach(() => {
// fixture = makeTestFixture();
// env = makeTestEnv();
// Component.env = env;
// document.head.innerHTML = "";
// });
// afterEach(() => {
// fixture.remove();
// });
//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------
describe.skip("styles and component", () => {
describe("styles and component", () => {
test("can define an inline stylesheet", async () => {
// class App extends Component {
// static template = xml`<div class="app">text</div>`;
// static style = css`
// .app {
// color: red;
// }
// `;
// }
// expect(document.head.innerHTML).toBe("");
// const app = new App();
// expect(document.head.innerHTML).toBe(`<style component=\"App\">.app {
// color: red;
// }</style>`);
// await app.mount(fixture);
// const style = getComputedStyle(app.el!);
// expect(style.color).toBe("red");
// expect(fixture.innerHTML).toBe('<div class="app">text</div>');
class App extends Component {
static template = xml`<div class="app">text</div>`;
static style = css`
.app {
color: red;
}
`;
}
expect(document.head.innerHTML).toBe("");
const app = await mount(App, fixture);
expect(document.head.innerHTML).toBe(`<style data-component=\"App\">.app {
color: red;
}</style>`);
const style = getComputedStyle(app.el as HTMLElement);
expect(style.color).toBe("red");
expect(fixture.innerHTML).toBe('<div class="app">text</div>');
});
test("inherited components properly apply css", async () => {
// class App extends Component {
// static template = xml`<div class="app">text</div>`;
// static style = css`
// .app {
// color: red;
// }
// `;
// }
// class SubApp extends App {
// static style = css`
// .app {
// font-weight: bold;
// }
// `;
// }
// expect(document.head.innerHTML).toBe("");
// const app = new SubApp();
// expect(document.head.innerHTML).toBe(`<style component=\"SubApp\">.app {
// font-weight: bold;
// }</style><style component=\"App\">.app {
// color: red;
// }</style>`);
// await app.mount(fixture);
// const style = getComputedStyle(app.el!);
// expect(style.color).toBe("red");
// expect(style.fontWeight).toBe("bold");
// expect(fixture.innerHTML).toBe('<div class="app">text</div>');
class App extends Component {
static template = xml`<div class="app">text</div>`;
static style = css`
.app {
color: red;
}
`;
}
class SubApp extends App {
static style = css`
.app {
font-weight: bold;
}
`;
}
expect(document.head.innerHTML).toBe("");
const app = await mount(SubApp, fixture);
expect(document.head.innerHTML).toBe(`<style data-component=\"App\">.app {
color: red;
}</style><style data-component=\"SubApp\">.app {
font-weight: bold;
}</style>`);
const style = getComputedStyle(app.el as HTMLElement);
expect(style.color).toBe("red");
expect(style.fontWeight).toBe("bold");
expect(fixture.innerHTML).toBe('<div class="app">text</div>');
});
test("inherited components properly apply css, part 2", async () => {
class App extends Component {
static template = xml`<div class="app"/>`;
static style = css`
.app {
color: tomato;
}
`;
}
class BetterApp extends App {}
class EvenBetterApp extends BetterApp {
static style = css`
.app {
background-color: papayawhip;
}
`;
}
expect(document.head.innerHTML).toBe("");
await mount(EvenBetterApp, fixture);
expect(document.head.innerHTML).toBe(`<style data-component=\"App\">.app {
color: tomato;
}</style><style data-component=\"EvenBetterApp\">.app {
background-color: papayawhip;
}</style>`);
});
test("get a meaningful error message if css helper is missing", async () => {
// class App extends Component {
// static template = xml`<div class="app">text</div>`;
// static style = `.app {color: red;}`;
// }
// let error;
// try {
// new App();
// } catch (e) {
// error = e;
// }
// expect(error).toBeDefined();
// expect(error.message).toBe(
// "Invalid css stylesheet for component 'App'. Did you forget to use the 'css' tag helper?"
// );
// });
// test("inline stylesheets are processed", async () => {
// class App extends Component {
// static template = xml`<div class="app">text</div>`;
// static style = css`
// .app {
// color: red;
// .some-class {
// font-weight: bold;
// width: 40px;
// }
// display: block;
// }
// `;
// }
// new App();
// expect(document.head.querySelector("style")!.innerHTML).toBe(`.app {
// color: red;
// }
// .app .some-class {
// font-weight: bold;
// width: 40px;
// }
// .app {
// display: block;
// }`);
class App extends Component {
static template = xml`<div class="app">text</div>`;
static style = `.app {color: red;}`;
}
let error;
try {
await mount(App, fixture);
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.message).toBe(
"Invalid css stylesheet for component 'App'. Did you forget to use the 'css' tag helper?"
);
});
test("inline stylesheets are processed", async () => {
class App extends Component {
static template = xml`<div class="app">text</div>`;
static style = css`
.app {
color: red;
.some-class {
font-weight: bold;
width: 40px;
}
display: block;
}
`;
}
await mount(App, fixture);
expect(document.head.querySelector("style")!.innerHTML).toBe(`.app {
color: red;
}
.app .some-class {
font-weight: bold;
width: 40px;
}
.app {
display: block;
}`);
});
test("properly handle rules with commas", async () => {
// const sheet = processSheet(`.parent-a, .parent-b {
// .child-a, .child-b {
// color: red;
// }
// }`);
// expect(sheet)
// .toBe(`.parent-a .child-a, .parent-a .child-b, .parent-b .child-a, .parent-b .child-b {
// color: red;
// }`);
class App extends Component {
static template = xml`<div/>`;
static style = css`
.parent-a,
.parent-b {
.child-a,
.child-b {
color: red;
}
}
`;
}
await mount(App, fixture);
expect(document.head.querySelector("style")!.innerHTML)
.toBe(`.parent-a .child-a, .parent-a .child-b, .parent-b .child-a, .parent-b .child-b {
color: red;
}`);
});
test("handle & selector", async () => {
// let sheet = processSheet(`.btn {
// &.danger {
// color: red;
// }
// }`);
// expect(sheet).toBe(`.btn.danger {
// color: red;
// }`);
// sheet = processSheet(`.some-class {
// &.btn {
// .other-class ~ & {
// color: red;
// }
// }
// }`);
// expect(sheet).toBe(`.other-class ~ .some-class.btn {
// color: red;
// }`);
class App extends Component {
static template = xml`<div/>`;
static style = css`
.btn {
&.danger {
color: red;
}
}
.some-class {
&.btn {
.other-class ~ & {
color: red;
}
}
}
`;
}
await mount(App, fixture);
expect(document.head.querySelector("style")!.innerHTML).toBe(`.btn.danger {
color: red;
}
.other-class ~ .some-class.btn {
color: red;
}`);
});
});