Look and feel problems encapsulating a ReactJS embedded application using Material ui - css

I am embedding a ReactJS application using Material UI styled components inside a Wordpress page. I am using webpack for transpilation of the js.
I have produced the bundle of the embedded version of the application and I am importing and instantiating it in the following way
<script src="https://<domain url>/path/myapp.app.bundle.js"></script>
<script type="text/javascript">
function loadApp() {
window.MyApp("my-app-div");
}
</script>
my-add-div corresponds to the id of the div where I want my application appears
Everything is working under the functional point of view.
My problem is related to the look and feel of the application that is influenced by the css of the wordpress theme.
I have tried to use the components CssBaseline and ScopedCssBaseline but the rendering seems to ignore them.
I have also tried those directives to reset the css styles but it did not work
#my-app-div {
all: initial; /* blocking inheritance for all properties */
}
#my-app-div * {
all: unset; /* allowing inheritance within #my-app-div */
}
I have found a possibile solution prefixing all the css of the Material UI style component with the id of the my-app-div div, in this way the css of the styled component has more priority than the css coming from the wordpress theme.
I have also found a plugin that could help in this but it is not compatible with the latest version of stylis https://www.npmjs.com/package/stylis-plugin-extra-scope
Does anyone has any suggestion to help me?

Have you tried adding a prefix to your material UI class for production build
using the following approach.
import { createGenerateClassName, StylesProvider } from '#material-ui/core'
const generateClassName = createGenerateClassName({
seed: 'xyz',
productionPrefix: 'abc-',
})
function App() {
return (
<StylesProvider generateClassName={generateClassName}>
...
</StylesProvider>
)
}

Related

Issue with :global() css-module selectors not being pure in NextJS

So I'm migrating an app from CRA to NextJS and I have encountered an error for the .module.scss files of some components and pages:
Syntax error: Selector ":global(.label-primary)" is not pure (pure selectors must contain at least one local class or id)
I get this error for all the :global and :local css-module selectors. Based on what I have searched I can fix this issue by wrapping the selector in a class and editing the jsx aswell. but wouldn't that defeat it's purpose?
And how is this working on the CRA version of the app and not on NextJS?
EDIT:
One solution I have for this is moving :global() selectors to the global css files that are imported in _app.js but my question is that is there any way that we can have so these styles would be usable like they are right now ( :global(...) )?
No there isn't any solution as of yet other than overriding the webpack config itself. It was working in CRA because they probably have mode: local, while Next.js has pure.
I haven't tried overriding css-loader webpack config, so I am simply suggesting a workaround. Since, you are using SCSS, you can wrap your pseudo-global [1] styles like this:
.root :global {
.foo {
color: red;
}
}
Now wrap your component/page in a div and set the class as styles.root on that element. Then, on all the child elements you can directly set className="foo".
import styles from "../styles/index.module.scss";
const IndexPage = () => (
<div className={styles.root}>
<div className="foo">This text should be red!</div>
</div>
);
export default IndexPage;
Note that, you need to consider issues regarding specificity after this method, also this doesn't directly work with animations, you need to separate the keyframes and then make them global.
Demo Sandbox
[1]: This method doesn't make the styles truly global as the styles are still scoped. The class foo will work only when some parent has styles.root as class. This is preferrable only if you didn't intend to use your :global(.selector) from other components, and were using them just because you wanted to manipulate the class names using JS without the styles object.
If you want these to be truly global, add styles.root to document.documentElement in an useEffect hook like this:
import { useEffect } from "react";
import styles from "../styles/index.module.scss";
const IndexPage = () => {
useEffect(() => {
document.documentElement.classList.add(styles.root);
return () => {
document.documentElement.classList.remove(styles.root);
};
}, []);
return (
<div className="foo">
This text should be red, even if you put it in another component until the
page is same. If you want it across pages inject it in _app or _document.
</div>
);
};
export default IndexPage;
Demo Sandbox
PS: Injecting class to html in _app or _document is not exactly same as using a global stylesheet, as it may happen that you have multi-page application, then only the CSS of the components on a particular page will be requested because of automatic CSS code-splitting done by Next.js. If that's not the case and all your pages share same CSS, then there is no need to complicate things, just go with the conventional method of importing styles in _app.
I had the same problem, the right writing is
.root:global {
color:red
}
Another approach is to make a container, wrap it, and carry it out like follows:
import style from '../styles/style.module.css'
<div className={styles.container}>
<p>Hello World</p>
</div>
.container p {
font-size: 20px;
}
And yes, you only need to include your tags and conditions in the CSS file if you have a lot of them.

Why does CSS styling disappear in React when directly changing route in browser URI?

I have a simple front-end React app created using npx create-react-app. The app is using react-router-dom routes. When I directly change the URI in the browser from say, localhost:3000/ to localhost:3000/search it will navigate to the <Route>but with no CSS rendered; just HTML from the component.
How can I make sure CSS is rendered in the new route when directly navigating in the browser? My future goal is to be able to copy and paste a route in a new tab and navigate to the correct page and display results from an API.
Css style sheets will need to be imported either at the root level or within the file itself.
style sheets need to be imported when used and then the corresponding classname will need to be used within the component or tag.
Another useful way to set react css without style sheets is by using in line styles
e.g
<div style ={{float: "right", textAlign : "center"}}> </div>
EDIT
A really easy way to get styles going within a react project is install bootstrap.
then buttons and stuff can be assigned classNames such as
<div className = "jumbotron"></>
this will leave a grey box around the items.
<div className = "btn btn-primary"></>
this will give you a blue styled button.
Any more information or help within your application let me know and provide some code snippets.
You can use styled-components. styled-components are widely used in ReactJs and React Native and it's a perfect choice.
styled-component : https://www.npmjs.com/package/styled-components
I realized that react apps created using npx create-react-app allow you to import a css module for components.
Given the component, Button.jsx, you can simply create a css module with the convention, [module-name].module.css. For the case of Button.jsx, create a file named Button.module.css, import "styles" from the module. Styles will be an object containing all the CSS styles.
I if I had a folder named "components" with all my components, I could make a folder within "components", say called "compStyles", and create all the [module-name].module.css files in there.
Button.module.css:
/* class names must be camelCased */
.myButton {
margin: 0 auto;
}
span {
fontSize: 20px;
}
If I had the above mentioned file structure, I could import and use like so:
import React from 'react';
import styles from './styles/Button.module.css';
const Button = () => {
return (
<div className={styles.myButton}>
<button><span>Some Button</span></button>
</div>
)
}
export default Button;
Styles for the span will be automatically applied, and any other class will be referenced by styles.className. Create one file for every component, and each component's CSS will take care of itself and not break like it would if it was in the public folder.

How to stylize web compoenent inside react app?

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.

Using jQuery and bootstrap JS with stenciljs components

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.

CSS Modules - referencing classes from other modules

I have understood the concept of CSS modules so much that I am convinced that I do not want to do anything else that that for the future.
Currently I am trying to refactor an existing app to use CSS modules, the app has used classic sass with BEM methodology since.
Before I describe my problem I want to make clear that I undestand that I am addressing an issue that is not really within the domain of CSS modules. One should apply styles solely for usage inside a single module. At the most one should compose CSS classes with other CSS classes of other modules. But basically: You build an (HTML-)module and you use CSS modules to style that module and that's that.
Here's the problem:
In the process of refactoring there is one single issue that derives from having had a SASS-based style system. I can't find a valid method to work with a CSS class within a CSS modules environment when this class should work in combination of another class from another module.
Example in SASS:
[page.scss]
.wrapper {
margin: 0;
}
[headline.scss]
.headline {
color: green;
}
.wrapper {
.headline {
color: orange;
}
}
As you can see: One module (page) defines a CSS class "wrapper", another module defines a CSS class "headline". And, additionally, the class "headline" should behave a bit differently when placed inside the class "wrapper".
Again, I know that this is not really the domain of CSS modules. But I really would like to know if this is somehow doable with CSS modules? The "composes"-feature of CSS modules does not really fit here...
This is a common issue when migrating to CSS Modules. In short, a css module cannot override a style from another css module, and this is by design. Styles are supposed to live with the components that render them, and nowhere else.
What you can do to refactor this is to create a component style variant and explicitly set the variant through a prop when rendered within your wrapper.
For example, suppose your headline component currently looks something like this:
CSS
.headline {
color: green;
}
JSX
import styles from "Headline.css";
const Headline = () => {
return (
<div className={styles.headline} />
);
}
Rather than trying to override the .headline class name from somewhere else, you can create a variant class name that you toggle through a prop:
CSS
.headline-green {
color: green;
}
.headline-orange {
color: orange;
}
JSX
import styles from "Headline.css";
const Headline = ({orange}) => {
return (
<div className={orange ? styles.headlineOrange : styles.headlineGreen} />
);
}
And when you render it from your wrapper, set it to the orange variant:
<Headline orange />
Tip: you can use composes to eliminate duplicate common styles between your variants.

Resources