I am currently writing a component in svelte and in order to work on only this component I am also using storybook.
The problem is that because of the css library I am using the component will be incorrectly rendered unless it is properly wrapped by a parent element. In short, this component is list element and without the list wrapper the css will be funky.
So the question is, can I somehow tell storybook to wrap my component in a div?
i.e. something like this
storiesOf("Kanban card", module)
.add(
"small",
() => ({
Component: Card,
template: "<div class='wrapper'><Card /></div>",
props: {
...
}
})
);
Your best bet would be to create a separate Svelte component, specifically for that story. This approach also gives you a way to use slots properly, something that's not clearly available through Storybook.
Something like kanban-card.story.svelte containing:
<script>
import Card from '../wherever/kanban-card.svelte';
// export the props that the component needs
// pass up all events you want to handle
</script>
<Card on:eventYouWant />
Related
I have my react app inside which i want to use vaadin-date-picker (v. 4.0.5).
I want to change some of the date pickers in a way that they would be above my modal by changing z-index to 1100 (for example)
and some to stay at 200.
In some examples, people put <style> tag inside
<vaadin-date-picker></vaadin-date-picker>
But react is not recognizing <style> attribute inside render function.
I can set class for
<vaadin-date-picker class='my-class'>
but it only changes control itself and not the backdrop part. Is there a way to change styles of some web components inside react app?
Make sure you are importing the file correctly
import './yourcssfile.css'; // if in same directory
also in react classes are applied using className keyword
<vaadin-date-picker className="my-class">
and lastly you have to follow the documentation vaadin. you can't add your own styles if the vaadin-date-picker doesn't support it.
Here in their documentation it says Vaadin Date Picker comes with both Lumo and Material design themes, and it can be customized to fit your application.
The cleanest way to use styles in my opinion is to use CSS files, so
import './classname.css';
Would be my default answer. However, if you want to set styles explicitly in your Component, these are two examples, how you can do it:
class Example extends Component {
getStyle1 = () => {
return {
fontWeight: 'bold'
};
}
render () {
return (
<div><div style={this.getStyle1()}>style1</div>
<div style={style2}>style2</div></div>
)
}
}
const style2 = {
fontStyle: 'italic'
}
Remember that it's JavaScript objects that you return with your styles, so you don't use CSS syntax with dashes (e.g. you type backgroundColor instead of background-color) and all values must be put in quotes.
We have application that is built using Angular and now for all customer-specific requirements we want to build web components using stencil js.
Idea is to keep core application clean and not to mixin code for specific client requirements.
So we came up to web components and we stick to Stencil JS.
First problem that we are facing is that our web component will need to use jquery, bootstrap js and some third party js as well.
We want to build our components to be encapsulated from outside that means they will be in shadow dom.
Now I have two questions
1) Is it good practice that every component include JS libraries like jQuery, bootstrap js etc because it doesn't sound to me as a good idea
2) How we can include jQuery for example into web component.
I tried many ways and the last one was to include tag in the constructor of a stencil web component, but it doesn't work.
export class TestComponent {
#Prop() token: string;
#State() test: string;
#Element() private element: HTMLElement;
constructor() {
this.element.innerHTML = `
<script src="../../assets/js/jquery.min.js"></script>
`;
So the question is how to use JS third party libraries in web component built in stencil that is in shadow dom ( shadow option is set to true )
Any opinions about this in general are welcome and will be appreciated :)
For me it sounds like you are bending the purpose of Stencil and Web-components a bit. I never had this pain doing this but to answer your question: It depends of what you wanna achieve. For example you can use Jquery natively inside the shadow-dom when you import Jquery in the Light-dom.
Index.html
<script src="/jquery.min.js"></script>
</head>
<body>
<my-component></my-component>
<div id="test2"></div>
</body>
my-component.tsx
testfunc(){
console.log($().jquery);
console.log($("#test"));
console.log($("#test2"));
}
render() {
return <div id="test">
<button onclick={this.testfunc.bind(this)}>asd</button>
</div>;
}
The result of testfunc are here:
So as you can see - you already can use Jquery just by putting it into your main application. But there are some limitations as you can see you have access to all the DOM elements from light-dom but none from shadow-dom. Thats why #test wasn't found and #test2 was.
But interesting to mention here is that I was also able to load a file inside this #test2 div container which is in the index.html. Just by using the jquery .load function from inside the web-component.
$( "#test2" ).load( "/index.html" );
Things get a bit more complicated when you want to use the $ selector to get elements inside the web-component (shadow-dom) but there is absolutely no reason to do so:
Stencil has it's own this.el.shadowRoot.querySelector() that you can use inside the component or you can directly stick a variable to a DOM element like so:
render() {
return <div ref={el => this.element = el}>
<button onclick={this.testfunc.bind(this)}>Press Button</button>
</div>;
}
Than you can use this.element inside the web-component to access the div.
But in general you can also try to use the scoped flag in the component decorator. Than you usually can use everything from the light dom because there is not such a hard isolation:
#Component({
tag: 'my-component',
styleUrl: 'my-component.css',
shadow: true ---> instead of this
scoped: true ---> try this
})
To summarize a bit: I think there is never a good reason to use these libraries inside a web-component in general. But if you really have to - it always depends on your needs and what you want / need to achieve. Stencil has some really helpful and good built in functions.
For most web components I would not use jQuery, as with any modern framework dom manipulation is not needed, mostly you just focus on render function.
I did a test and this seems to work ok after adding jquery dependency with npm i jquery:
import { Component, h } from '#stencil/core';
import $ from "jquery";
#Component({
tag: 'app-test'
})
export class AppTest {
render() {
const version = $().jquery;
return [
<div>
jQuery Version: {version}
</div>
];
}
}
I would consider using Stencil exactly the opposite way. Create your generic stuff in Stencil and than the customer specific inside Angular. For example you start with a simple "button" as a web-component in stencil.
export class CoolestButton {
render() {
return <button class="coolest-button"></button>
}
}
Than you create another web-component "dropdown" which uses the "button" as foundation.
export class CoolestDropdown {
...
someDropdownFunctions(){}
...
render() {
...
return [<coolest-button></coolest-button>, ...dropdownSpecific]
}
}
Than you create a Header component which exists of dropdowns.
export class CoolestHeader {
render() {
return [
<coolest-dropdown data="NavPoint1,NavPoint2,NavPoint3">Home</coolest-dropdown>,
<coolest-dropdown data="About-us,Impress">Home</coolest-dropdown>
];
}
The amazing thing of Stencil is that you can use 10000x times the coolest-button but it will only will load it once. Thats why nesting the web-components like this is absolutely coding sugar. Than you have a really strong library that you can use in all your customer projects. And when you write your tests - every customer application is tested too since they have all the same base.
jQuery can be used inside a Shadow DOM with out any issues using stenciljs.
If the jQuery has already been added to the light DOM or not has to be checked inside the web component.
In some cases when we load the jQuery again it resets the events in the light DOM creating issues in Light DOM.
You can use a console.log($) and try and catch to check if its loaded other wise load.
$ will also work if you install 'types' for jQuery.
So basically it only matters if the external libraries tries to access the DOM using
document.getElementById
inside the library files.
Inside shadow DOM
this.el.shadowroot
has to be used. The associated css should be added inside the webcomponent as well.
I have four classnames, lets call them red-0, red-1, blue-0, blue-1
The CSS for red-0 and red-1 is the same, and the CSS for blue-0 and blue-1 is the same.
My React component are changing the classNames just as I want. My problem is that it seems like the React component ignores the CSS styling when I apply a classname with the same CSS it had right before the state change. Is this an actual case? Because my CSS changes perfectly when it goes from e.g. red-0 to blue-0, but not from red-0 to red-1.
If you have a suggestion of how I can solve this problem, I would be greatful.
A buddy helped me:
The trix was to add a promise and first set the state to an empty classname, and then add the classname that i wanted inside the promise, like this:
this.setState({ className: '' }, () => this.setState({ className: 'red-0' }));
The reason for having four classnames instead of two was because I hoped the classname change would trigger a new animation, but it did not, even though it triggered new regular CSS changes. I ended up using only two classnames: red and blue
This challenge is best illustrated with a little example:
import React from 'react'
import styles from './styles.pcss'
const Spinner = () => (
<article className={styles.spinner}>
{/* <div> elements here*/}
</article>
)
export default Spinner
I'd like to change the position of the spinner component so this has to be done by the parent of the spinner. I see 3 solutions:
Use a CSS rule targeting the <article> tag (the root element of the spinner). I don't like this as the parent component is making assumptions about the internal structure of the spinner. If this tag changes, then all styles targeting it will break. I must be able to change the internal structure of my components without worrying about breaking many areas of my app.
Pass a className down to the spinner component. It's much better as no assumption is made regarding the internals of the spinner. But as potentially every component could be positioned by their parent (a very common CSS task), I would have then to implement the className being passed down (incl. prop types validation, etc) for every component that would need custom styling. There must be a better solution.
import React, { PropTypes } from 'react'
import classNames from 'classnames'
import styles from './styles.pcss'
const Spinner = ({ className }) => (
<article className={classNames(styles.spinner, className)}>
{/* <div> elements here*/}
</article>
)
Spinner.propTypes = {
className: PropTypes.string
}
export default Spinner
Use a containing <div> around the component I want to style:
<div className="spinner"><Spinner /></div>
But this leads to bloated markup with unnecessary <div>s in situations where many elements have to be styled (like setting their positions).
What are your recommendations?
Thx
I'm leaning towards solution 3. This way, the styled component (spinner) stays clean and doesn't have to implement additional logic for getting a className, validating it and merging it with its own classes.
I wanted to see how 3rd party libraries implemented this scenario, and they favor solution 3 as well. Here's the related discussion about styling <FormattedMessage> of react-intl.
Furthermore, the wrapping <div> would only be used when styling the container itself (typically, the position), not the contents of the component. And if many components have to be positionned, it's very likely that they are subcomponents, ex: <TodoItemList> and in this case the <TodoList> parent component would pass down the className to them as they won't exist outside this particular context.
I develop component, but I need, what would can change the tag name of a component container.
Like this:
<my-component tagName="section"></my-component>
render to
<section>... my inner component tags ...</section>
How to make it?
I read about render() and functional components, but create component template with
render(createEl, ctx){
return createEl(ctx.props.tagName, [createEl('div', [...])])
}
... looks horrible and uncomfortable.
edit:
Something like this:
http://jsbin.com/qozawiwumi/edit?html,output
I decided this question, using is="" and this.$vnode.data.tag for render custom component wrapper.
https://jsbin.com/gifesom/edit?html,output