The correct way to bundle CSS modules - css

I have a weird thing happening when my css modules are exported with the * as styles becomes inaccessible when I bundle my code and use it in other repo's.
The response from styles when bundled:
{default: {... my class names} }
When I change my code to import styles from '...' it works when bundled because styles is the default but fails the tests because styles does not have access to the named exports.
rollup config.js
import resolve from '#rollup/plugin-node-resolve'
import commonjs from '#rollup/plugin-commonjs'
import typescript from 'rollup-plugin-typescript2'
import { terser } from 'rollup-plugin-terser'
import postcss from 'rollup-plugin-postcss'
import postCssConfig from '#cinch-labs/postcss-config'
import pkg from './package.json'
import { designTokens, toJSON } from './src/tokens'
const extensions = ['.ts', '.tsx']
// stylelint does work but the postcss one needed to be removed
const postcssPlugins = postCssConfig(toJSON(designTokens)).filter(
({ postcssPlugin }: { postcssPlugin: string }) => postcssPlugin !== 'stylelint',
)
export default [
{
input: './src/index.ts',
output: [
{
file: pkg.main,
format: 'cjs',
},
{
file: pkg.module,
format: 'es',
},
],
plugins: [
postcss({
modules: true,
extract: false,
syntax: 'postcss-scss',
plugins: postcssPlugins,
use: ['sass'],
}),
resolve({
extensions,
}),
commonjs(),
typescript({ tsconfig: 'tsconfig.rollup.json' }),
terser(),
],
external: ['react', 'react-dom'],
},
]
test.component.tsx
import React from 'react'
import classNames from 'classnames'
// I expected the bundler to resolve this for me...
import * as styles from './text.module.scss'
import { TextProps } from './text.types'
export const Text: React.FC<TextProps> = ({
children,
fontSize = 'm',
fontWeight = 'medium',
fontStyle = 'normal',
lineHeight = 'body',
element = 'p',
className,
...props
}) => {
const HtmlEl = element
const classes = classNames(
{
[styles[`textSize${fontSize.toUpperCase()}`]]: fontSize,
[styles[`textWeight${fontWeight.toUpperCase()}`]]: fontWeight,
[styles[`textLineHeight${lineHeight.toUpperCase()}`]]: lineHeight,
[styles[`textFontStyle${fontStyle.toUpperCase()}`]]: fontStyle,
},
className,
)
// classes returns undefined when bundled because of commonjs format.
return (
<HtmlEl className={classes} {...props}>
{children}
</HtmlEl>
)
}
I know this is due to the way common JS works however I would expect for the import * as styles to work. When I change it to import styles from './text.module.scss' it works fine when bundled but does not work in tests.

Using import * as styles from './text.module.scss' you are importing the styles as a named export.
Since this also returns {default: {... my class names} }, you can use styles.default instead, or, perhaps, assign it to a new variable like
const style = styles.default

Fixing this issue was by doing import styles from 'path name' and then installing jest-css-modules to map the styles object in my test.
https://www.npmjs.com/package/jest-css-modules

for me to compile and include with rollup.js the scss into the bundle/build worked adding:
plugins: [
postcss({
modules: true,
extract: false,
syntax: 'postcss-scss',
use: ['sass'],
}),
],
Hope this will help someone else in this journey :)

Related

How to add vue plugins to Vite?

when i was using webpack instead of vite i used to write this code in app.js
// for File uploads
import vueFilePond from "vue-filepond";
import "filepond/dist/filepond.min.css";
// image preview in file pond
import FilePondPluginImagePreview from "filepond-plugin-image-preview";
import "filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css";
import FilePondPluginFilePoster from "filepond-plugin-file-poster";
import "filepond-plugin-file-poster/dist/filepond-plugin-file-poster.css";
// file size validations
import FilePondPluginFileValidateSize from "filepond-plugin-file-validate-size";
import FilePondPluginFileValidateType from "filepond-plugin-file-validate-type";
const FilePond = vueFilePond(
FilePondPluginImagePreview,
FilePondPluginFilePoster,
FilePondPluginFileValidateSize,
FilePondPluginFileValidateType
);
but now with Vite this is not working.
i tried adding
export default defineConfig({
plugins: [
laravel({
input: [
'resources/js/app.js',
'resources/css/app.css',
],
refresh: true,
}),
vue({
template: {
transformAssetUrls: {
base: null,
includeAbsolute: false,
},
},
}),
vueFilePond *****************************this************
],
resolve: {
alias: {
'$': 'jQuery',
},
},
});
this is not working. Please help!
So this worked for me!
// for File uploads
import vueFilePond from "vue-filepond";
import "filepond/dist/filepond.min.css";
// image preview in file pond
import FilePondPluginImagePreview from "filepond-plugin-image-preview";
import "filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css";
import FilePondPluginFilePoster from "filepond-plugin-file-poster";
import "filepond-plugin-file-poster/dist/filepond-plugin-file-poster.css";
// file size validations
import FilePondPluginFileValidateSize from "filepond-plugin-file-validate-size";
import FilePondPluginFileValidateType from "filepond-plugin-file-validate-type";
const FilePond = vueFilePond(
FilePondPluginImagePreview,
FilePondPluginFilePoster,
FilePondPluginFileValidateSize,
FilePondPluginFileValidateType
);
Vue.use(AnyComponent) ; THis was not working in my case
So i tried adding component in below code. this worked for me.
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')),
setup({ el, app, props, plugin }) {
return createApp({ render: () => h(app, props) })
.use(plugin)
.use(ZiggyVue, Ziggy)
**.mixin({ components: { FilePond } })**
.mount(el);
},
});

Vuetify 3 and storybook 6 - styles and components do not work

I´ve been researching a lot about Vuetify + Storybook integration.
I installed Vuetify using Vite and then installed the storybook/vue3 plugin using:
npx sb init --builder storybook-builder-vite
The problem is that every component that I created is not using the Vuetify styles. I read here that I need to extend the Storybook app so my preview.js looks like:
// .storybook/preview.js
import { app } from '#storybook/vue3';
import vuetify from '../src/plugins/vuetify'
app.use(vuetify);
export const decorators = [
(story) => ({
components: { story },
template: '<v-app><story /></v-app>',
}),
];
But this doesn't seem to work. It doesn't throw any errors in the terminal and not in the browser but the vuetify styles and components do not work. My components are being render normally with Vuetify if I try to run the app itself.
Any ideas? I´m totally lost. Maybe there´s no support since Vuetify 3 is on alpha? Or this is a storybook issue?
Here´s my repo if you want to check it out https://github.com/heyimnowi/vue-ui-library
OMG I cannot believe I solve it by just doing this:
const path = require('path')
const vuetify = require('#vuetify/vite-plugin'); // THIS
module.exports = {
"stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.#(js|jsx|ts|tsx)"
],
"addons": [
"#storybook/addon-links",
"#storybook/addon-essentials"
],
"framework": "#storybook/vue3",
"core": {
"builder": "storybook-builder-vite"
},
async viteFinal(config, { configType }) {
config.resolve.alias['~storybook'] = path.resolve(__dirname)
config.resolve.alias['#'] = path.resolve(__dirname, '..', 'src')
config.css = {
preprocessorOptions: {
scss: { additionalData: `#import "src/styles/application.scss";` },
},
};
config.plugins = [
...config.plugins,
vuetify() // THIS
];
return config;
}
}

How to setup storybook at root level lerna monorepo

I am working on a project which is set up with lerna mono repo, we have multiple stencilJS projects for an individual component inside packages of monorepo.
My project sructure is:
I am new to the storybook, now I have to set up the storybook at the root level which all the packages storybook.
I followed an article on the internet and I have set up something which works only for a single package component, due to the current style of setup.
Due to defineCUstomElements in preview.js it is loading the first project package loader I am able to see only the first project stories. Css is not loading for second project stories.
Can someone help me to set up a storybook at the root level which works for all packages?
My example
storybook/main.js
module.exports = {
"stories": [
"../stories/**/*.stories.mdx",
"../packages/plugin-*/src/components/plugin-*/*.stories.#(js|jsx|ts|tsx)"
],
"addons": [
'#storybook/addon-links',
'#storybook/addon-essentials',
'#storybook/addon-viewport',
'#storybook/addon-notes',
'#storybook/addon-docs'
]
}
storybook/preview.js
import { defineCustomElements } from '../packages/stencilProj1/loader';;
defineCustomElements();
export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
};
package/stencilProj1/component1.stories.ts
import readme from './readme.md'
import React from 'react';
import ComponentButton from '../../../dist/collection/components/ComponentButton /ComponentButton';
export default {
title: 'Button',
component: ComponentButton,
argTypes: {
label: { control: 'text' },
type: { type: 'select', options: ['primary'] },
disabled: { control: 'boolean' }
},
parameters: {
markdown: readme
},
};
const Template = ({ label, type, disabled = false }) => {
return <component-button type={type} disabled={disabled}>{label}</component-button>;
};
export const Primary = Template.bind({});
Primary.args = {
type: 'primary',
label: 'Primary Button',
};
After a couple of months of attempts, I finally solved this puzzle :) And want to share my solution with community
I have a lerna v4 monorepo for react v17 + mui v5 components written in typescript and flavored with storybook v6 and webpack v5
mui has its wrappers in preview.js, that's why I added the path to .storybook in babel-loader
module.exports = {
core: {
builder: "webpack5",
},
framework: "#storybook/react",
stories: ["../components/**/*.stories.#(ts|tsx)"],
addons: [
"#storybook/addon-links",
"#storybook/addon-essentials",
{
name: "#storybook/preset-create-react-app",
options: {
craOverrides: {
fileLoaderExcludes: ["less"],
},
},
},
],
webpackFinal: config => {
const {
module: {
rules: [, , , , , { oneOf }],
},
} = config;
const babelLoader = oneOf.find(({ test }) => new RegExp(test).test(".ts"));
babelLoader.include = [/components\/(.*)\/src/, /.storybook/];
babelLoader.options.sourceType = "unambiguous";
return config;
},
};
it is also worth to mention my tsconfig has these lines
"rootDirs": [
"./src",
],
Have you tried to import project 2's defineCustomElement with "as" to rename it and use it?
(something along the line of following inside preview.js)
import { defineCustomElements } from '../packages/stencilProj1/loader';
import { defineCustomElements as defineSecondProject } from '../packages/stencilProj2/loader';
defineCustomElements();
defineSecondProject();
This is very manual and even if this works, if you have many repo might not be good solution but I've done something similar to load multiple component loaders and that seem to work OK for my use case in the past.

Storybook custom webpack loading empty scss objects

I added a custom webpack.config.js file to my .storybook project so that I can import .scss files. This is what I added, straight from the storybook docs.
const path = require('path');
// Export a function. Accept the base config as the only param.
module.exports = (storybookBaseConfig, configType) => {
// configType has a value of 'DEVELOPMENT' or 'PRODUCTION'
// You can change the configuration based on that.
// 'PRODUCTION' is used when building the static version of storybook.
// Make whatever fine-grained changes you need
storybookBaseConfig.module.rules.push({
test: /\.scss$/,
loaders: ["style-loader", "css-loader", "sass-loader"],
include: path.resolve(__dirname, '../src')
});
// Return the altered config
return storybookBaseConfig;
};
Here's my story:
import React from 'react';
import { storiesOf } from '#storybook/react'; // eslint-disable-line import/no-extraneous-dependencies
import { action } from '#storybook/addon-actions'; // eslint-disable-line import/no-extraneous-dependencies
import { linkTo } from '#storybook/addon-links'; // eslint-disable-line import/no-extraneous-dependencies
import Button from './'
import ButtonStyles from './index.scss'
import ButtonCompareTrayStyles from './compare-tray.scss'
import ButtonCompareRemminderStyles from './compare-reminder.scss'
console.log({ButtonStyles, ButtonCompareTrayStyles, ButtonCompareRemminderStyles})
storiesOf('Button', module)
.add('with text', () => <Button onClick={action('clicked')}>Hello Button</Button>)
.add('with some emoji', () => <Button onClick={action('clicked')}>😀 😎 👍 💯</Button>)
.add('with default styles', () => <Button styles={ButtonStyles} onClick={action('clicked')}>Hello World</Button>)
.add('with CompareTray styles', () => <Button styles={ButtonCompareTrayStyles} onClick={action('clicked')}>Hello World</Button>)
.add('with CompareRemminder styles', () => <Button styles={ButtonCompareRemminderStyles} onClick={action('clicked')}>Hello World</Button>)
When I log some Button styles, it appears that each one of these objects is empty.
Why are these objects empty? How can I get scss working with storybook?
For everyone who has the same problems, I added the package #storybook/preset-scss and configured it the following way:
module.exports = {
"stories": [
"../src/**/*.stories.*",
"../src/**/*.story.*"
],
"addons": [
"#storybook/addon-links",
"#storybook/addon-actions",
"#storybook/addon-essentials",
"#storybook/addon-knobs",
{
name: '#storybook/preset-scss',
options: {
cssLoaderOptions: {
modules: true
}
}
},
]
}
That's it.

Imported styles object is empty in Jest

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

Resources