Can't access global SCSS variables in Storybook with Webpack + React - javascript

Trying to get access to global SCSS variables like I have in the rest of my app. I've taken it so far but I'm currently getting this error with the .storybook.scss test file;
The .storybook directory and the src directory are siblings.
-/.storybook
|-config.ts
|-storybook.scss
|-webpack.config.js
-/src
|-/components/
|-/scss/
|-global.scss
ERROR in ./src/components/Global/Dialog/dialog.scss (./node_modules/react-scripts/node_modules/css-loader??ref--8-1!./node_modules/postcss-loader/src??postcss!./node_modules/sass-loader/lib/loader.js??ref--8-3!./node_modules/style-loader!./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/lib/loader.js!./src/components/Global/Dialog/dialog.scss)
Module build failed (from ./node_modules/sass-loader/lib/loader.js):
.modal {
^
Invalid CSS after "": expected 1 selector or at-rule, was "var content = requi"
in /Users/hghazni/Sites/eat/src/components/Global/Dialog/dialog.scss (line 1, column 1)
I've attempted to use many different webpack.config.js and config.ts configurations and followed the official documentation on the Storybook website but had no luck.
webpack.config.js
const { resolve } = require('path');
module.exports = {
module: {
rules: [
{
test: /\.scss$/,
loaders: ['style-loader', 'css-loader', 'sass-loader'],
include: path.resolve(__dirname, '../src/scss')
},
{
test: /\.css$/,
loader: 'style-loader!css-loader',
include: __dirname
}
]
}
}
config.ts
import { configure } from "#storybook/react";
require('./storybook.scss');
const req = require.context('../src/', true, /.stories.tsx$/);
function loadStories() {
req.keys().forEach((filename) => req(filename))
}
configure(loadStories, module);
.storybook.scss
body {
background: $color-primary;
}
Dialog.tsx
import React from 'react'
import './dialog.scss';
type DialogType = {
onClose: any;
isOpen: any;
}
const Dialog: React.SFC<any> = props => {
let dialog: any = (
<div className={"dialog " + props.dialogClass}>
<button className={"dialogClose"} onClick={props.onClose}>X</button>
<div>{props.children}</div>
</div>
);
if (!props.isOpen) {
dialog = null;
}
return (
<div className="modal">
{dialog}
</div>
)
}
export default Dialog;
dialog.scss
.modal {
position: absolute;
top: 20%;
left: 50%;
-webkit-transform: translate(-50%);
transform: translate(-50%);
width: 95%;
#include media-query(min, $desk-start) {
width: 70%;
max-width: $desk-start;
}
}
.dialog {
border: 4px dashed rgba(47, 144, 189, 0.411);
border-radius: $half-spacing;
padding: $base-spacing;
background: $color-base;
}
.dialogClose {
position: absolute;
top: 0;
right: 0;
padding: $half-spacing;
background: $color-base;
border: 1px solid rgba(47, 144, 189, 0.411);
color: rgba(47, 144, 189, 1);
border-radius: 50px;
padding: $half-spacing 12.5px;
&:hover {
cursor: pointer;
}
}
I'm looking for any support/guidance on how to get storybook to be able to load my SCSS variables located in ../src/scss/global.scss.

So what Storybook or the documentation doesn't explicitly tell you is that having two webpack configs isn't ideal. You'll need to basically cater for a lot of the things needed in your main webpack config if you're trying to mirror your components.
Though there is a way to have to one webpack config with all the options separated so you can reuse it across the site as many times as you need. I even wrote an article about how to do it and built a boiler plate template for you with it already setup if you want to check it out.
https://levelup.gitconnected.com/a-killer-storybook-webpack-config-a0fd05dc70a4

Had same kind of issue, then found that Storybook supports scss configuration out of the box, I removed all rules related to sass/scss from storybook webpack config override and everything worked. However npm i node-sass was needed

Related

how to import into a component vue the global stylesheet?

I'm starting my first project in VueJS and following the documentation and tutorials I wanted to create a global stylesheet in order to use the same styles in different components. I have installed scss loader and created my stylesheet
$mainFont: 'PoppinsRegular, Arial';
$mainFontSemiBold: 'PoppinsSemiBold, Arial';
$mainFontItalic: 'PoppinsRegularItalic, Arial';
$primaryColor: #fff;
$secondaryColor: #dce0e6;
$tertiaryColor: #f0f4f7;
$quaternaryColor: #233240;
$quinaryColor: #0e5fa4;
$senaryColor: #14a30f;
$septenaryColor: #cd3c2d;
$octonaryColor: #6C757D;
$undenaryColor: #7e848c;
$duodenaryColor: #19b4c2;
I have imported this style sheet into main.js as indicated in the documentation, trying two different ways
import Vue from 'vue'
import App from './App.vue'
import './assets/scss/_variables.scss'
// this is the second way I've tested// require('./assets/scss/_variables.scss')
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
and after importing it into the main.js I have modified in my 'home' component the header of the style section and imported one of the colors declared in my style sheet
<style lang='scss' rel='./assets/scss/_variables'>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: $septenaryColor;
}
</style>
There's no doubt that I'm doing something wrong because the console throws out an error that it doesn't recognize the color reference in my styles
"Module build failed (from ./node_modules/sass-loader/dist/cjs.js):
SassError: Undefined variable: "$septenaryColor".
on line 56 of src/components/HelloWorld.vue
color: $septenaryColor;
---------^"
Someone who can help me out and make me see the error of my ways.
Thank you very much in advance for your help and time
To achieve this you have to modify your vue.config.js.
module.exports = {
css: {
loaderOptions: {
sass: {
prependData: `
#import "#/style/index.scss"; // path to file with your global styles
`
}
}
}
};
Then all styles from this file will be accessible from every component.
More about this you can rad here: Globally Load SASS into your Vue.js Applications

Styled Components Nested components erroring

I am trying to workout why this is erroring.
Although If I do not have CardWrapper wrapping around CardImage the image is displaying.
import React from 'react'
import styled, { css } from 'styled-components'
const CardWrapper = styled.div`
background-color: yellow;
border-color: 1px solid red;
position: relative;
`
const CardImage = styled.img`
height: auto;
width: 100%;
`
const Card = props => {
return (
<CardWrapper>
<CardImage src={props.data.imageUrl}/>
</CardWrapper>
)
}
export default Card;
App.js
<Card data={{imageUrl: 'https://via.placeholder.com/630x354', logoUrl: "https://via.placeholder.com/100x100", text: "test"}}/>
Error
./src/Components/Card/Card.js
Error: Cannot find module '/Users/max/test/test/test/node_modules/babel-preset-react-app/node_modules/#babel/core/lib/index.js'. Please verify that the package.json has a valid "main" entry
Looks like you need to install a Node.js package. In your terminal, navigate to the project's root directory and run:
npm install babel-preset-react-app

Same code generating different snapshots on different machines

We're using git for version control, so the code is the same. But if I generate snapshots, and my coworkers run the tests, they all fail on the snapshot part. Why can this happen?
Example component
import React from 'react';
import styled from 'styled-components';
import classnames from 'classnames';
import { colors } from '../../utils/css';
const ProgressIcon = ({ className, progress, color }) => (
<div className={className}>
<div className={classnames('background', color)}>
<div className={classnames('icon', progress, color)}/>
</div>
</div>
);
export const StyledProgressIcon = styled(ProgressIcon)`
width: 12.8px;
height: 12.8px;
margin: 0;
div {
margin: 0;
}
.background.white {
border: 2px solid ${colors.LG_WHITE};
}
.background.gray {
border: 2px solid ${colors.LG_GRAY_2};
}
.background {
height: 100%;
width: 100%;
border-radius: 50%;
box-sizing: border-box;
.icon {
height: 100%;
}
.icon.white {
background: ${colors.LG_WHITE};
}
.icon.gray {
background: ${colors.LG_GRAY_2};
}
.icon.full {
width: 100%;
}
.icon.half {
width: 50%;
}
.icon.empty {
width: 0;
}
}
`;
Test
import React from 'react';
import { shallow } from 'enzyme';
import { StyledProgressIcon as ProgressIcon } from '../ProgressIcon';
describe('<ProgressIcon/>',
() => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<ProgressIcon progress={'full'} color={'gray'}/>);
});
it('should match the snapshot', () => {
expect(wrapper).toMatchSnapshot();
});
});
I'm comparing the snapshots created by my coworkers (Everybody else's tests are passing with the exact same snapshots, and code. It only fails on my machine)
Here is the log
FAIL src/components/ProgressIcon/test/ProgressIcon.test.js
● <ProgressIcon/> › should match the snapshot
expect(received).toMatchSnapshot()
Snapshot name: `<ProgressIcon/> should match the snapshot 1`
- Snapshot
+ Received
## -4,11 +4,11 ##
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "sc-bdVaJa",
- "isStatic": false,
+ "isStatic": true,
"rules": Array [
"
width: 12.8px;
height: 12.8px;
margin: 0;
## -69,11 +69,10 ##
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "sc-bdVaJa",
"target": [Function],
"toString": [Function],
- "usesTheme": false,
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
10 | });
11 | it('should match the snapshot', () => {
> 12 | expect(wrapper).toMatchSnapshot();
| ^
13 | });
14 | });
15 |
at Object.toMatchSnapshot (src/components/ProgressIcon/test/ProgressIcon.test.js:12:23)
And the reverse is if I generate snapshots, and my coworkers test. Why is this happening and how can I fix this?
There is a version mismatch in your styled-components lib dependency. As explained
here
It is the styled component's shallow render that shows you that "isStatic": false value
Both of you need to sync up your dependencies. First
make sure that both have the same package.json.
Then the surefire way to do this is. In one of your computers
Remove node_modules
delete package-lock.json
Run npm install
Commit your package-lock.json! (ignore if no changes)
Go to all other PCs.
Pull in the changes to package lock json (reject all local and accept all remote changes).
Remove node_modules.
Run npm install.
Now run your tests and check, the snapshots should be equal.
I fixed it by installing https://github.com/styled-components/jest-styled-components
Although I've followed the above mentioned points as well but I think this one should also fix the issue.
yarn add --dev jest-styled-components
Usage
import React from 'react'
import styled from 'styled-components'
import renderer from 'react-test-renderer'
import 'jest-styled-components'
const Button = styled.button`
color: red;
`
test('it works', () => {
const tree = renderer.create(<Button />).toJSON()
expect(tree).toMatchSnapshot()
expect(tree).toHaveStyleRule('color', 'red')
})

Why is my CSS not applying to my React components?

Say I'm creating a React app and have some CSS for components. I've added the style-loader and css-loader to my webpack config here:
module.exports = {
mode: 'development',
entry: './client/index.js',
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-react']
}
}
}, {
test: /\.css$/,
loader: 'style-loader'
}, {
test: /\.css$/,
loader: 'css-loader',
query: {
modules: true,
localIdentName: '[name]__[local]___[hash:base64:5]'
}
}
]
},
resolve: {
extensions: ['*', '.js', '.jsx']
},
output: {
path: __dirname + '/dist',
publicPath: '/',
filename: 'bundle.js'
},
devServer: {
contentBase: './dist'
},
devtool:"#eval-source-map"
};
I have a simple CSS file just to test on one component:
.list-group-item {
border: 1px solid black;
outline-style: solid;
outline-color: red;
outline-width: medium;
}
In my app I'm applying the classname to a element in my component and importing my CSS
import React, { Component } from 'react'
import { connect } from 'react-redux'
import selectContact from '../actions/action_select_contact'
import { bindActionCreators } from 'redux'
import '../styles.css';
class ContactList extends Component {
renderList() {
return this.props.contacts.map((contact) => {
return (
<li
key={contact.phone}
onClick={() => this.props.selectContact(contact)}
className='list-group-item'>{contact.firstName} {contact.lastName}</li>
);
});
}
render() {
return (
<ul className = 'list-group col-sm-4'>
{this.renderList()}
</ul>
);
}
}
function mapStateToProps(state) {
return {
contacts: state.contacts
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ selectContact: selectContact }, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(ContactList)
I'm also importing the CSS file in the same way in the ContactList component itself. The rest of the project is here. I would have expected to see an outline around my comtactsList but there is not. There is no CSS when I inspect the page. Why is this not getting applied?
In a react project created with create-react-app or npx create-react-app, I also had the same issue.
I had imported index.css file in my App Component but my styles were not being applied properly to my React Components.
I even tried directly adding index.css file in my html file in the public folder and using link tag to link to my index.css file (which resides within src folder).
<link rel="stylesheet" href="./../src/index.css">
That also didn't work.
Finally, I read an article about 7 ways to apply CSS into React. One best way was to install node-sass into our project and use index.scss ( import './index.scss') into App Component instead of index.css.
And Hurray!!! My CSS worked fine, All the Media Queries started to work fine.
Below is the code snippet you can try.
import React from "react";
import ReactDom from "react-dom";
import './index.scss';
// --- Data to work with ---
const books = [
{
id: 1,
name: 'The Rudest Book Ever',
author: 'Shwetabh Gangwar',
img: 'https://m.media-amazon.com/images/I/81Rift0ymZL._AC_UY218_.jpg'
},
{
id: 2,
name: 'The Rudest Book Ever',
author: 'Shwetabh Gangwar',
img: 'https://m.media-amazon.com/images/I/81Rift0ymZL._AC_UY218_.jpg'
},
{
id: 3,
name: 'The Rudest Book Ever',
author: 'Shwetabh Gangwar',
img: 'https://m.media-amazon.com/images/I/81Rift0ymZL._AC_UY218_.jpg'
},
{
id: 4,
name: 'The Rudest Book Ever',
author: 'Shwetabh Gangwar',
img: 'https://m.media-amazon.com/images/I/81Rift0ymZL._AC_UY218_.jpg'
},
];
const Book = ({ book }) => {
return (
<div className={"book"}>
<img src={book.img} alt="book image" />
<h3>{book.name}</h3>
<p>{book.author}</p>
</div>
)
};
const Books = () => {
return (
<main className={"books"}>
{
books.map(book => {
return (<Book book={book} key={book.id} />)
})
}
</main>
)
};
// Work a bit fast | one step at a time
const App = () => {
return (
<main>
<h2>Books</h2>
<Books />
</main>
)
}
ReactDom.render(<App />, document.getElementById("root"));
/* --- Mobile First Design --- */
.books{
text-align: center;
};
.book{
border: 1px solid #ccc;
text-align: center;
width: 200px;
padding: 1rem;
background: #001a6e;
color: #fff;
margin:auto;
};
h2{
text-align: center;
}
/* --- Adding Media Queries --- */
#media only screen and (min-width: 900px){
.books,.persons{
display:grid;
grid-template-columns: 1fr 1fr;
}
}
To install node-sass, simple do npm install node-sass --save
Then rename all your .css files with .scss and your project with work properly.
The package.json should have the node-sass dependency added as shown below:
"dependencies": {
"node-sass": "^4.14.1",
"react": "^16.8.3",
"react-dom": "^16.8.3",
"react-scripts": "2.1.5"
},
Hope this will help many developers :)
It would be helpful to see your React component as well.
Given this code, you are passing className as a property into the component rather than assigning a class to it:
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
data: null
};
}
render() {
return (
<div>
/** This line specifically **/
<ContactList className="contactList" />
<ContactDetail />
<AddContactModal />
</div>
);
}
}
Inside your component, you would need to use className={this.props.className} on a regular HTML tag inside of your component in order to pass the className through.
You can add your specific css file to index.js directly. (like this import './master.css'
)
According to the react documentation here the className can be applied to all DOM regular element such as <div>, <a>, ...
Does your CSS works with the regular tag <div className='contactList'>hello</div> ?
Styling React Using CSS
I had a problem with applying css file to my react component, which solved by adding a .module.css extension to my css file name. I found the answer here in w3schools

How to apply styles to the react component via css modules

Ive got a problem with applying styles to my React Components.
I generated template to my project via Yeoman. It created a lot of config files, not only webpack.config.js and because of that I'm a little bit confused. As Readme file of react-generator says it support out of the box some features without installing it:
*Different supported style languages (sass, scss, less, stylus)
*Style transformations via PostCSS
According to this I show part of cfg/default.js
'use strict';
const path = require('path');
const srcPath = path.join(__dirname, '/../src');
const dfltPort = 8000;
function getDefaultModules() {
return {
preLoaders: [{
test: /\.(js|jsx)$/,
include: srcPath,
loader: 'eslint-loader'
}],
loaders: [
{
test: /\.css$/,
loader: 'style-loader!css-loader!postcss-loader'
},
{
test: /\.sass/,
loader: 'style-loader!css-loader!postcss-loader!sass-loader?outputStyle=expanded&indentedSyntax'
},
{
test: /\.scss/,
loader: 'style-loader!css-loader!postcss-loader!sass-loader?outputStyle=expanded'
},
{
test: /\.less/,
loader: 'style-loader!css-loader!postcss-loader!less-loader'
},
{
test: /\.styl/,
loader: 'style-loader!css-loader!postcss-loader!stylus-loader'
},
I think this part is responsible for converting .scss files.
So in my todosStyle.scss file I've got this code:
.todos {
display: flex;
flex-flow: row wrap;
justify-content: center;
width: 100%;}
.input {
display: block;
text-align: center;}
.button {
width: 60px;
margin: 5px; }
.label {
display: block;
border: solid 1px; }
Base on this styling I want to style my component Todos.jsx:
import React from "react";
import Todo from "./Layout/Todo.jsx";
import TodoStore from "../../stores/TodoStore.jsx";
import * as TodoActions from "../../actions/TodoActions.jsx";
import styles from '../../styles/todosStyle.scss';
export default class Todos extends React.Component {
constructor() {
super();
this.getAllTodos = this.getAllTodos.bind(this);
this.state = {
todos: TodoStore.getAll(),
};
}
getAllTodos(){
this.setState({
todos: TodoStore.getAll(),
});
}
componentWillMount(){
TodoStore.on('change', this.getAllTodos);
}
componentWillUnmount(){
TodoStore.removeListener('change', this.getAllTodos);
}
createTodo(){
if (this.refs.addTodo.value != ''){
TodoActions.createTodo(this.refs.addTodo.value);
this.refs.addTodo.value = '';
}
}
deleteTodo(todo){
TodoActions.deleteTodo(todo);
}
completeTodo(todo){
TodoActions.completeTodo(todo);
}
render() {
const {todos} = this.state;
const TodoComponents = todos.map(todo =>{
return <Todo key={todo.id} {...todo} completeTodo={this.completeTodo.bind(this, todo)} deleteTodo={this.deleteTodo.bind(this, todo)}/>;
});
return (
<div>
<h1>Todos</h1>
<div class = {styles.input}>
<label class= {styles.label}>Add new Todo</label>
<input ref="addTodo"/>
<button class={styles.button} onClick = {this.createTodo.bind(this)}><i class="fa fa-plus-square-o"></i></button>
</div>
<ul class={styles.todos} class="row">{TodoComponents}</ul>
</div>
);
}
}
but styling isn't applied, although I've imported todosStyle.scss
Could someone help me?
Just a note, its good to add $ on your loader test properties eg. test: /.scss$/, to only match end of file i believe.
i havent seen this before "import styles from '../../styles/todosStyle.scss';"
this kind of says that you are exporting styles from your sass file, perhaps import * as style ... might work, but i haven't used this approach.
try this.
require ('../../styles/todosStyle.scss');
this will bundle your css with the javascript.
then just use the class as you normally would on your render function.
also note; if you would like to create a separate file for your css, you can do that with plugins.
You're using css modules but you haven't turned them on in your Webpack config:
loader: 'style-loader!css-loader!postcss-loader'
becomes
loader: 'style-loader!css-loader?modules!postcss-loader'
As per the css-loader readme https://github.com/webpack/css-loader#css-modules.

Categories

Resources