How to inline/include css in a react library package - reactjs

I'm trying to build a react component library that I can use in other applications and I'm running into an issue of packaging css/less with the component library.
in the component library package:
component.tsx
import React, { ReactElement } from 'react';
import styles from "./component.less";
const TestComponent: React.FC = (): ReactElement => {
....
return (
<div className={styles.root}>
....
</div>
);
};
export default TestComponent;
component.less
#bg-color: #428bca;
.root {
background-color: #bg-color;
}
I run the below commands to build the library:
> tsc --emitDeclarationOnly
> lessc src/component.less dist/library.css
> babel src --out-dir dist --extensions .ts,.tsx --source-maps
> copyfiles -u 1 src/**/*.less dist/
and in the dist directory get this:
> index.d.ts
> index.js
> library.css
> component.less
> ....
in a different application I just install the library and use the component:
import TestComponent from "library-package";
<TestComponent /> // in the render somewhere
and when I start this application the component is rendered but the css/less code is not applied and not imported.
I'd like to include some default css inside of the library package. And I know that I could just include the library.css in the index.tsx of an application and the css could work, but I don't want the other application to have to include a css file or have a less loader to handle the css if it doesn't use less itself.
I assume that the problem currently is that the application doesn't know how to handle .less files from the library because it doesn't have a less loader setup (although I don't get an error about anything)
tldr: an application would just install the component library, include the component in a render and the component will render with some css (preferrably using less in the library). Is that possible to do?

I guess I was looking for something like the styled-components but using less + minifying instead of writing the CSS in a JavaScript file and wrapping HTML tags.
With the styled-components library I got it to work this way:
component.tsx
import React, { ReactElement } from "react";
import cssesc from "cssesc";
import styled, { ThemeProvider, DefaultTheme } from "styled-components";
export interface TestComponentTheme extends DefaultTheme {
backgroundColor?: string;
}
const DEFAULT_THEME: TestComponentTheme = {
backgroundColor: "#428bca",
};
const StyledDiv = styled.div`
background-color: ${props => cssesc(props.theme.backgroundColor)};
`;
interface TestComponentInput {
theme?: TestComponentTheme;
}
const TestComponent: React.FC<TestComponentInput> = ({theme}: TestComponentInput ): ReactElement => {
....
let componentTheme: TestComponentTheme = Object.assign({}, DEFAULT_THEME);
if (theme) {
componentTheme= Object.assign({}, componentTheme, theme);
}
return (
<ThemeProvider theme={componentTheme}>
<StyledDiv>
....
</StyledDiv>
</ThemeProvider>
);
};
export default TestComponent;
building the library requires just two steps now:
> tsc --emitDeclarationOnly
> babel src --out-dir dist --extensions .ts,.tsx --source-maps
and there are no CSS files in the dist directory anymore:
> index.d.ts
> index.js
> ....
in a different application I just install the library and use the component (which renders with the CSS inline):
import TestComponent from "library-package";
<TestComponent /> // in the render somewhere
and optionally I can overwrite the CSS of the component:
import TestComponent, { TestComponentTheme } from "library-package";
const customTheme: TestComponentTheme = {
backgroundColor: "#000fff",
};
<TestComponent theme={customTheme} /> // in the render somewhere

Related

Can not use ubuntu-mono font in chakra-UI

I'm trying to use the ubuntu-mono font in react with chakra-UI. So, I referred to the chakra-UI [docs][1].
However, the change could not be reflected.
My ```theme.ts``` and ```App.tsx``` are below.
theme.ts
import { extendTheme } from "#chakra-ui/react";
const theme = extendTheme({
fonts: {
heading: "Ubuntu-mono",
body: "Ubuntu-mono",
},
styles: {
global: {
body: {
backgroundColor: "black",
color: "white",
}
}
}
})
export default theme;
App.tsx
import * as React from "react"
import {
ChakraProvider, Container,
} from "#chakra-ui/react"
import {BrowserRouter} from "react-router-dom";
import theme from "./theme/theme";
import '#fontsource/ubuntu-mono/700.css';
import {Router} from "./router/Router";
export const App = () => (
<ChakraProvider theme={theme}>
<BrowserRouter>
<Router/>
</BrowserRouter>
</ChakraProvider>
)
Of course, I have run npm install with the package.json that includes the "#fontsource/ubuntu-mono": "^4.5.6" line in its dependencies.
I also referred to another doc of the chakra-UI, however, I could not find out describes regarding this problem.
Although, this may an easy problem, anyone who gives me a solution.
I'm guessing this has to do with the environment. I tried it initially in CodeSandbox but the font didn't load but when I ran it locally using Vite app, it worked just fine.
What environment are you working in? See my repo here: https://github.com/estheragbaje/test-fonts

React testing library can't read styles using Tailwind CSS classes

I have a simple React component that will initially have a Tailwind CSS class of hidden which apply CSS display: none and will change the class to visible on button click.
When I test with expect().not.toBeVisible() it tells me the element is already visible while it has a hidden class.
If I don't use Tailwind CSS and use a normal style={{display: 'none'}} it'll correctly identify that the element isn't visible. That means clearly the issue is with Tailwind CSS.
Here's my test:
test("Notification bar should be initially hidden but visible on click", async () => {
render(<Notifications />);
expect(await screen.findByTestId("list")).not.toBeVisible();
// this test fails while the element already has a Tailwind CSS class of "hidden"
});
While this's my component:
<ul className="hidden" data-testid="list">
<li>item 1</li>
</ul>
The solution explained in this Stack Overflow: cannot check expectelm not tobevisible for semantic ui react component. Based on that thread, I extend the solution to make it works with TailwindCSS as the steps explained below,
Project structure
root/
src/
test/
index.css
test-utils.tsx
component.test.tsx
index.css
1. Generate CSS from the TailwindCSS template files
By issuing the command below, the CSS file called index.css will be generated in src/test directory
npx tailwindcss -i ./src/index.css -o ./src/test/index.css
Further reading: TailwindCSS installation
2. Create custom render function
Next we need to inject the generated CSS file into the JSDOM. Custom render function will be useful so we won't be needed to repeat this task for each test
import { render, RenderOptions } from '#testing-library/react';
import React, { FC, ReactElement } from 'react';
import fs from 'fs';
const wrapper: FC<{ children: React.ReactNode }> = ({ children }) => {
return <>{children}<>;
};
const customRender = (ui: ReactElement, options?: Omit<RenderOptions, 'wrapper'>) => {
const view = render(ui, { wrapper, ...options });
const style = document.createElement('style');
style.innerHTML = fs.readFileSync('src/test/index.css', 'utf8');
document.head.appendChild(style);
return view;
};
export * from '#testing-library/react';
export { customRender as render };
Further reading: Testing Library Setup
3. Perform testing, unit test suppose to be success now
import React from 'react';
import { render, screen } from './test-utils';
test('Renders hidden hello world', () => {
render(<span className="hidden">Hello World</span>);
expect(screen.getByText('Hello World')).not.toBeVisible();
});
Why souldn't we use toHaveClass matchers instead?
it wouldn't align with the Testing Library guiding principle of “emphasize a focus on tests that closely resemble how your web pages are interacted by the users“ because by doing so, you are interacting with the component unnaturally

How export styles folder when create my library

I'm creating a custom react library. I'm using typescript, redux, and create-react-library to create it.
It' all ok, I can import my library from other projects, but my problem is that when I do npm run build. This generates a dist folder but doesn't add styles folder with scss and other files from fontello.
I have to create and script that when the post-build copy styles folder into the dist folder.
Also, in index.d.ts file of my library I have this import: import "./styles/main.scss"; but doesn't import it. (Maybe I have to add something in tsconfig.json)
import "bootstrap/dist/css/bootstrap.css";
import "./styles/main.scss";
import React from "react";
import MainTree from "./components/main";
import HierarchyTreeReducerState from "./redux/reducers/HierarchyTreeReducer";
import TreeReducerState from "./redux/reducers/TreeReducer";
interface Props {
portal: boolean;
removeBranchWithChildren: boolean;
data?: any;
}
function TreeLibrary({ portal, removeBranchWithChildren, data }: Props) {
return (
<MainTree
removeBranchWithChildren={removeBranchWithChildren}
data={data}
portal={portal}
/>
);
}
export { TreeLibrary, HierarchyTreeReducerState, TreeReducerState };
The only solution I have found is to import the main.scss from the project that imports the library, like this:
import "react-tree-library/dist/styles/main.scss";
App.tsx from a project that imports my library:
import React from "react";
import { TreeLibrary } from "react-tree-library";
import "react-tree-library/dist/styles/main.scss";
import { completeData } from "./mockedData/huge";
class App extends React.Component<any, any> {
render() {
return (
<TreeLibrary
portal={false}
data={completeData}
removeBranchWithChildren={false}
/>
);
}
}
export default App;
But I would like that when import my library I don't need to import styles manually.
How can I solve this?

import components from create-react-library subfolder

I have been trying to use create-react-library and so far it works, but I can only import components successfully from index.js. If I try to make another file , I recieve an import error.
The file structure is as such
example
\ Node Module
\ public
\ src
| App.js
| index.js
...
src
\ Patterns
| index.js
| button.js
Currently I can only successfully import components from index.js of the main src. Is there a way to successfully import components from folders such as Patterns or another file?
\ App.js ( example )
Importing button gives me an error "Cant import button from neo"
import React from 'react'
import { ExampleComponent,Button} from 'neo'
import {Test} from 'neo/Patterns';
import 'neo/dist/index.css'
const App = () => {
return (
<>
<Test />
<Button text='Click me' />
<ExampleComponent text="Create React Library Example 😄" />
</>
)
}
export default App
Please check if this is what you're trying to achieve.
index.js will be exporting required components like this,
import ExampleComponent from './ExampleComponent/ExampleComponent';
// ExampleComponent is placed inside a folder named ExampleComponent
import Patterns from './Patterns/Patterns';
// Patterns is placed inside a folder named Patterns
export { ExampleComponent, Patterns };
Patterns.js can look like this,
import React from 'react'
const Patterns = () => {
return <div>Patterns Component sample</div>
}
export default Patterns;
ExampleComponent.js can look like this,
import React from 'react'
import styles from './styles.module.css'
const ExampleComponent = ({ text }) => {
return <div className={styles.test}>Example Component: {text}</div>
}
export default ExampleComponent;
In the consumer level (in this case, example folder), in any jsx, like App.js you can refer those exported components like,
import { ExampleComponent, Patterns } from 'neo'
return (
<Patterns />
)

How do I pass my components theme through storybook?

I need to pass a theme that is used in components but I get a syntax error.
My .storybook/config.js:
import { configure, addDecorator } from '#storybook/react'
import React from 'react'
import theme from '../src/theme'
import { ThemeProvider } from 'styled-components'
import '../src/styles/index.css'
addDecorator(story => <ThemeProvider theme={theme}>{story()}</ThemeProvider>)
function loadStories() {
const req = require.context('../src', true, /\.stories.js$/)
req.keys().forEach(filename => req(filename))
}
configure(loadStories, module)
Here's the full error:
Have you tried making the theme provider as a separate file without using the decorators? The below is a Styled-components and typescript implementation.
import React from 'react';
export const Container = ({ title, children }) => {
return (
<StoryWrapper>
<GlobalStyle theme={themes.default} />
<ThemeProvider theme={themes.default}>
<MainWrapper>
<Header>{title}</Header>
{children}
</MainWrapper>
</ThemeProvider>
</StoryWrapper>
);
};
I never used the add decorator feature and this was the config implementation I used, it is set up for tsx though.
import { addParameters, configure, addDecorator } from '#storybook/react';
import { withKnobs } from '#storybook/addon-knobs';
const req = require.context('../src', true, /.stories.tsx$/);
function loadStories(){
req.keys().forEach(req);
}
addDecorator(withKnobs);
configure(loadStories, module);
Seems like Babel is confused that you're using JSX in a JS file.
Try renaming config.js to config.jsx. The file extension should instruct babel to treat it as a JSX file.

Resources