I'm trying to implement a Lit component with some scss styling. Below is the component as it is right now:
import { html, LitElement } from 'lit-element';
import { ScopedElementsMixin } from '#open-wc/scoped-elements';
// Components
import 'inputmessage';
// Styles
import styles from './input-styles.scss';
export default class Input extends ScopedElementsMixin(LitElement) {
constructor() {
super();
}
static get properties() {
return {
label: { type: String, attribute: 'label' },
id: { type: String, attribute: 'id' },
value: { type: String, attribute: 'value' },
statusMessage: { type: String, attribute: 'status-message' },
statusType: { type: String, attribute: 'status-type' },
required: { type: Boolean, attribute: 'required' },
placeholder: { type: String, attribute: 'placeholder' },
type: { type: String, attribute: 'type' },
};
}
static get scopedElements() {
return {
'inputmessage': customElements.get('inputmessage'),
};
}
static get styles() {
return [styles];
}
render() {
return html`
<div>
${this.label && html`<label class="input-label" for="${this.id}">${this.label}</label>`}
<input type="${this.type}" required="${this.required}" value="${this.value}" placeholder="${this.placeholder}" id="${this.id}" name="${this.id}" />
</div>
`;
}
}
The CSS styles are in scss and only include the .input-label class. Now when I try to render the component on the screen it doesn't appear and I see the following message in the console output:
It seems the styles are not being picked up for some reason. I added the lit-scss-loader in my dependencies, but that also doesn't work. Anyone knows what I should do?
You need to use css tagged template function and unsafeCSS(str) function to make use of CSS imported into a string:
import { html, LitElement, css, unsafeCSS } from 'lit-element';
// later, inside your component class:
static get styles() {
return css`${unsafeCSS(styles)}`;
}
I have no clue what translates your SCSS, stopped using pre-processors years ago.
I can't comment so write new answer.
Lit don't process SCSS file.
If you need library check this library.
lit-scss-loader
other solution:
Convert scss to css manually then use this code.
import styles from './my-styles.css' assert { type: 'css' };
class MyEl extends LitElement {
static styles = [styles];
}
Note : above solution only work with chromium based browser.
Wait for other browser support.
Related
I am working with precompiled stylesheet (from SASS) and only need to toggle classes.
I have two elements that will be writing to an event. Based on the event being true/false I want to to toggle a class on my component.
Would this work:
import { LitElement, html } from 'lit-element'
/**
*
*
* #export
* #class MenuMainButton
* #extends {LitElement}
*/
export class MenuMainButton extends LitElement {
static get properties() {
return {
name: { type: String },
toggled: { type: String }
}
}
constructor() {
super()
this.name = 'Menu'
this.toggled = ''
this.addEventListener('toggle-main-menu', this.handleEvents)
}
render() {
return html`
<a #click=${this._onClick} class="menu-button wk-app-menu-button app-menu-open ${this.toggled} govuk-body"
>${this.name}</a
>
`
}
handleEvents(event) {
this.toggled = event.toggle ? 'hide-item' : ''
}
_onClick() {
const toggleMainMenu = new CustomEvent('toggle-main-menu', {
toggle: this.toggled === '' ? 1 : 0
})
this.dispatchEvent(toggleMainMenu)
}
}
window.customElements.define('main-menu-button', MenuMainButton)
One way to make styles dynamic is to add bindings to the class or style attributes in your template.
The lit-html library offers two directives, classMap and styleMap, to conveniently apply classes and styles in HTML templates.
Styles - LitElement
I cannot understand how to pass host component CSS class to a children element. I created a custom element:
...
const CUSTOM_INPUT_VALUE_PROVIDER: Provider = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => FormFieldComponent),
multi: true,
}
#Component({
moduleId: module.id,
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [CUSTOM_INPUT_VALUE_PROVIDER],
selector: 'form-field',
template: `
<div>
<input
(change)="onChange($event.target.value)"
(blur)="onTouched()"
[disabled]="innerIsDisabled"
type="text"
[value]="innerValue" />
</div>
`
})
export class FormFieldComponent implements ControlValueAccessor {
#Input() innerValue: string;
innerIsDisabled: boolean = false;
onChange = (_) => {};
onTouched = () => {};
writeValue(value: any) {
if (value !== this.innerValue) {
this.value = value;
}
}
registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
setDisabledState(isDisabled: boolean) {
this.innerIsDisabled = isDisabled;
}
get value(): any {
return this.innerValue;
}
set value(value: any) {
if (value !== this.innerValue) {
this.innerValue = value;
this.onChange(value);
}
}
}
And then use it like this in some reactive form:
<form-field formControlName="title"></form-field>
Problem: I added some validation in FormBuilder to title form control and when it not pass validation, Angular add classic css classes to form-field element: ng-pristine ng-invalid ng-touched.
How i can pass this CSS classes from host element to my input element in form-field component?
It is not duplicate of Angular 2 styling not applying to Child Component. Changing Encapsulation does not resolve the problem.
I think there's a way to do what you want by just knowing the angular classes of the hosting elements and not necessarily passing them down.
If so, your work-around would look something like this in the css of the custom form element:
:host.(ng-class)>>>HTMLelement {
property: value
}
Example:
:host.ng-valid>>>div {
border-left: 5px solid #42a948;
}
The ":host" part of this allows us to use the hosting (parent) html elements
The ">>>" is the deep selector that allows us to apply these styles to all children matching selection property (in this case, we're looking for div elements)
I have a component:
import React from 'react';
import * as styles from './RedComponent.css';
class RedComponent extends React.Component {
render () {
return <div className={ styles.red }></div>;
}
}
This is the test case:
describe('Test suite', () => {
test('RedComponent tests', () => {
const wrapper = shallow(<RedComponent />);
});
console.log(wrapper.debug());
gives
<div className={[undefined]}></div>
instead of
<div className="RedComponent__red"></div>
If I console the imported styles I get
console.log(styles); // {default: {}}
This is only in Jest test cases. Style is not undefined when the app renders in browser.
My jest config:
{
"moduleFileExtensions": [
"js"
],
"moduleDirectories": [
"node_modules"
],
"moduleNameMapper": {
"\\.(css|less)$": "identity-obj-proxy"
},
"setupFiles": [
"./test-setup.js"
],
"collectCoverageFrom": [
"src/**/*.{js}",
"!**/node_modules/**"
],
"testEnvironment": "node",
"transform": {
"^.+\\.js$": "babel-jest",
"\\.(md|ttf|txt|eot|ico|otf|svg|png|gif|woff2|woff|jpeg)$": "./file-transformer.js"
}
}
Using jest v21.2.1, identity-obj-proxy v3.0.0 and React v16.0.0.
You have to change this line
import * as styles from './RedComponent.css';
to this:
import styles from './RedComponent.css';
I assume that you are using css-loader or similar and this is just how the loader works.
Maybe worths to check the example:
https://github.com/keyanzhang/jest-css-modules-example/
I think your moduleNameMapper should be like this:
"^.+\\.(css|less)$": "identity-obj-proxy"
Create a jest/identity-obj-proxy-esm.js file with the following content:
// This works around the fact we use ES named exports for styles, e.g.:
// import * as styles from './styles.scss'.
// https://github.com/keyanzhang/identity-obj-proxy/issues/8
module.exports = new Proxy(
{},
{
get: function getter(target, key) {
if (key === '__esModule') {
// True instead of false to pretend we're an ES module.
return true;
}
return key;
},
},
);
Edit jest.config.js:
// jest.config.js
module.exports = {
...
moduleNameMapper: {
...
'\\.(css|scss)$': '<rootDir>/jest/identity-obj-proxy-esm.js',
}
};
🏆 João Vieira and https://github.com/keyz/identity-obj-proxy/issues/8#issuecomment-430241345
What is the proper way to use the styles option/property of the ComponentDecorator? Using the styles property with the default my-name component from the repository stencil-component-starter doesn't seem to affect the styles of the respective component nor generate something like a <style> tag in the <head>. How is styles intended to work? Or has it not been implemented yet? If the goal is to avoid having a separate CSS asset that needs to be loaded, but provide styles for the component, would styles be the right choice or is there another property such as host would need to be used?
Below is a sample component generated from the stencil-component-starter]1 with stylesUrl #Component property replaced with a styles property and setting font-size property. No errors are generated during dev or build tasks.
import { Component, Prop } from '#stencil/core';
#Component({
tag: 'my-name',
styles: `my-name { font-size: 24px; }`
})
export class MyName {
#Prop() first: string;
render() {
return (
<div>
Hello, my name is {this.first}
</div>
);
}
}
ComponentDecorator is defined as:
export interface ComponentOptions {
tag: string;
styleUrl?: string;
styleUrls?: string[] | ModeStyles;
styles?: string;
shadow?: boolean;
host?: HostMeta;
assetsDir?: string;
assetsDirs?: string[];
}
Thank you for any help you can provide!
I just tried with latest version 0.0.6-22, and it seems to work completely now.
While compiling, it will tell you if your styles contents is valid or not (mainly looking for valid selectors).
Here is a working example (with a simple string):
import { Component, Prop } from "#stencil/core";
#Component({
tag: "inline-css-example",
styles: 'inline-css-example { font-size: 24px; }'
})
export class InlineCSSExampleComponent {
#Prop() first: string;
render() {
return <div>Hello, my name is {this.first}</div>;
}
}
This one works too, with ES6 Template Strings (just showing multiline):
import { Component, Prop } from "#stencil/core";
#Component({
tag: "inline-templatestring-css-example",
styles: `
inline-templatestring-css-example {
font-size: 24px;
}
`
})
export class InlineCSSExampleComponent {
#Prop() first: string;
render() {
return <div>Hello, my name is {this.first}</div>;
}
}
(EDITed to show evolution since 0.0.6-13)
I have a minimal React component which consists of two files: button.jsx and button.less. The styles are imported and the class names are appended with a hash to make all styles local to the component.
This is great, but i'd like to have all component code in one file. Is it possible to inline the styles in jsx file without losing css modularity?
Current Code
button.jsx
import React from 'react';
import styles from './button.less'
export default class Button extends React.Component {
render() {
return <button className={styles.primary}>{this.props.text}</button>;
}
}
button.less
#import '~semantic-ui/src/definitions/elements/button.less';
.common {
composes: ui button;
}
.primary {
composes: common primary;
}
webpack.config.js (relevant bits)
module: {
loaders: [
{
test: /\.jsx$/,
loader: 'babel'
},
{
test: /\.less$/,
loader: "style!css?modules&importLoaders=1!less"
}
]
},
What i'd like to write instead
button.jsx
<style lang="less" modules>
#import '~semantic-ui/src/definitions/elements/button.less';
.common {
composes: ui button;
}
.primary {
composes: common primary;
}
</style>
import React from 'react';
export default class Button extends React.Component {
render() {
return <button className={styles.primary}>{this.props.text}</button>;
}
}
Inspired by vue.js and vue-loader.
I believe this is a duplicate of this unanswered question:
Using css-loader inline with Webpack + React
I wrote a Webpack loader for this very purpose:
https://github.com/chrisdavies/stylextract-loader
It allows you to write a single style tag per JSX file and it supports webpack CSS modules, too, if you want.
At build time, it extracts the rules from your style tag and moves them out to an external CSS file.
I should note that because it simply extracts your rules out to an external CSS file, it plays nice with SASS, autoprefixer, etc
You can use callback-loader for this. This is actualy a workaround, but it do the trick. Just implement a callback which will extract your css-code and replace it with appropriate import. For example:
webpack.config.js
var fs = require('fs');
var cssIndex = 0;
// Do not forget to create and clean temporary folder "cssTemp" before
var webpackConfig = {
...
resolve: {
alias: {
cssTemp: path.resolve('./cssTemp')
}
},
module: {
loaders: [
{ test: /\.jsx$/, loader: "callback!babel" }
]
},
callbackLoader: {
cssCallback: function(code) {
var filename = cssIndex + '.less';
cssIndex++;
// Save the css code from the callback argument
fs.writeFileSync('./cssTemp/' + filename, code);
// Return the import statement which will replace the callback statement
return 'import styles from "cssTemp/' + filename + '";';
}
}
...
};
button.jsx
import React from 'react';
cssCallback(`
#import '~semantic-ui/src/definitions/elements/button.less';
.common {
composes: ui button;
}
.primary {
composes: common primary;
}
`);
export default class Button extends React.Component {
render() {
return <button className={styles.primary}>{this.props.text}</button>;
}
}