My goal is to import additional input.css file that has styling for input form for my react components file to the default app.css. For some reason, it didnt detect the focused attribute that is applied on the input.css styling, but whenever I put the styling inside #layer components it works.
resources/css/app.css
#tailwind base;
#tailwind components;
#tailwind utilities;
#import "input.css";
.flex::before,
.flex::after {
display: none !important;
}
#layer components {
[type="text"],
[type="email"],
[type="url"],
[type="password"],
[type="number"],
[type="date"],
[type="datetime-local"],
[type="month"],
[type="search"],
[type="tel"],
[type="time"],
[type="week"],
[multiple],
textarea,
select {
border-color: transparent;
}
[type="text"]:focus,
[type="email"]:focus,
[type="url"]:focus,
[type="password"]:focus,
[type="number"]:focus,
[type="date"]:focus,
[type="datetime-local"]:focus,
[type="month"]:focus,
[type="search"]:focus,
[type="tel"]:focus,
[type="time"]:focus,
[type="week"]:focus,
[multiple]:focus,
textarea:focus,
select:focus {
border-color: transparent;
--tw-ring-color: transparent;
}
}
resources/css/input.css
.input-primary {
#apply focus:bg-form-bg bg-form-bg focus:outline-alerange focus:outline-none;
}
.input-error {
#apply ring ring-red-600;
}
.input-primary-outline {
#apply bg-[#fff] focus:bg-[#fff] border-alerange focus:border-alerange;
#apply file:bg-alerange file:text-white file:rounded-md file:pd-2;
}
resources/js/Input.jsx
import React, { useEffect, useRef } from 'react';
import PropType from 'prop-types';
Input.propTypes = {
type: PropType.oneOf(['text', 'email', 'password', 'number', 'file']),
name: PropType.string,
value: PropType.oneOfType([PropType.string, PropType.number]),
defaultValue: PropType.oneOfType([PropType.string, PropType.number]),
className: PropType.string,
variant: PropType.oneOf(['primary', 'outline', 'primary-outline']),
autoComplete: PropType.string,
required: PropType.bool,
isFocused: PropType.bool,
handleChange: PropType.func,
placeholder: PropType.string,
isError: PropType.bool,
}
export default function Input({
type = 'text',
name,
value,
defaultValue,
className,
variant = "primary",
autoComplete,
required,
isFocused,
handleChange,
placeholder,
isError
}) {
const input = useRef();
useEffect(() => {
if (isFocused) {
input.current.focus();
}
}, []);
return (
<div className="flex flex-col items-start">
<input
type={type}
name={name}
value={value}
defaultValue={defaultValue}
className={
`rounded-2xl bg-form-bg py-[13px] px-7 w-full ${isError && "input-error"} input-${variant} ${className}`
}
ref={input}
autoComplete={autoComplete}
required={required}
onChange={(e) => handleChange(e)}
placeholder={placeholder}
/>
</div>
);
}
There's actually a warning from the vite.js
enter image description here
But when I tried to move the #import on top before #tailwinds, it give another error on the webpage like this:
enter image description here
And it works for example when I write it like this:
resources/css/app.css
#tailwind base;
#tailwind components;
#tailwind utilities;
/* #import "input.css"; */
.flex::before,
.flex::after {
display: none !important;
}
.input-primary {
#apply focus:bg-form-bg bg-form-bg focus:outline-alerange focus:outline-none;
}
.input-error {
#apply ring ring-red-600;
}
#layer components {
[type="text"],
[type="email"],
[type="url"],
[type="password"],
[type="number"],
[type="date"],
[type="datetime-local"],
[type="month"],
[type="search"],
[type="tel"],
[type="time"],
[type="week"],
[multiple],
textarea,
select {
border-color: transparent;
}
[type="text"]:focus,
[type="email"]:focus,
[type="url"]:focus,
[type="password"]:focus,
[type="number"]:focus,
[type="date"]:focus,
[type="datetime-local"]:focus,
[type="month"]:focus,
[type="search"]:focus,
[type="tel"]:focus,
[type="time"]:focus,
[type="week"]:focus,
[multiple]:focus,
textarea:focus,
select:focus {
border-color: transparent;
--tw-ring-color: transparent;
}
.input-primary-outline {
#apply bg-[#fff] focus:bg-[#fff] border-alerange focus:border-alerange;
#apply file:bg-alerange file:text-white file:rounded-md file:pd-2;
}
}
help are appreciated, thanks.
I just found a work around on this, so instead of adding the import on the app.css file, you actually import the other css in the resources/js/app.jsx instead. Such as:
import "./bootstrap";
import "../css/app.css";
import "../css/button.css";
import "../css/input.css";
import "../css/sidebar.css";
import React from "react";
import { render } from "react-dom";
import { createInertiaApp } from "#inertiajs/inertia-react";
import { InertiaProgress } from "#inertiajs/progress";
import { resolvePageComponent } from "laravel-vite-plugin/inertia-helpers";
const appName =
window.document.getElementsByTagName("title")[0]?.innerText || "Laravel";
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) =>
resolvePageComponent(
`./Pages/${name}.jsx`,
import.meta.glob("./Pages/**/*.jsx")
),
setup({ el, App, props }) {
return render(<App {...props} />, el);
},
});
InertiaProgress.init({ color: "#4B5563" });
I dont know if this is actually the way, or best practices or such. But it works for now.
I have a basic ElipseDetail component that looks like the image below
<ElipseDetail text="1.07" />
When I am using the component everything works as expected.
But now I want to reuse the component in another place but add an extension to the Text component style
How can I achieve that with styled-components and reuse the component but change the Text which is a child?
import React, { ReactElement } from "react";
import styled from "styled-components";
interface Props {
text: string;
children?: React.ReactNode;
}
export const Container = styled.div`
padding: 4px 12px;
border-radius: 30px;
background-color: #eaeef2;
display: inline-block;
`;
export const Text = styled.p`
font-size: 12.5px;
font-weight: normal;
font-stretch: normal;
font-style: normal;
letter-spacing: 0.63px;
color: #687c97;
`;
export default function ElipseDetail({ text, children }: Props): ReactElement {
return (
<Container>
<Text>{text}</Text>
{children}
</Container>
);
}
Since ElipseDetails is not a styled component, but calls to a styled one, you can do something like:
function ElipseDetail({ text, children }: Props): ReactElement {
return (
<Container>
<Text>{text}</Text>
{children}
</Container>
);
}
ElipseDetail.Styled = Container;
export default ElipseDetail
And then, in a different component, you can change it like so:
const StyledElipseDetail = styled(ElipseDetail.Styled)`
${Text} {
//
}
`;
...
return <StyledElipseDetailed>...</StyledElipseDetail>
PS - I have taken this approach from an older question of mine which I found quite useful.
I would suggest to create a container and if Text is in that container then add different properties. & here means this class name so it evaluates to Text so when text would be in ElipseContainer then it will behave in different way depending on your use case. Here if it's wrapped with ElipseContainer then colour of Text is green and it's red otherwise.
Here is sandbox link :
sandobx
const ElipseContainer = styled.div``;
const Text = styled.p`
color: red;
${ElipseContainer} & {
color: green;
}
`;
const App =() =>
<ElipseContainer>
<ElipseDetail text="word2" />
</ElipseContainer>
I have two components TextField and Label.
The TextField is passing the prop req to the Label. I want to modify the styled-component based on the req prop being passed in. Here is my current code that is not working.
No errors are being reported to the console.
TextField.js
import React, {Component} from 'react';
import styled from 'styled-components';
import Label from '../Label/Label';
const Wrapper = styled.div`
display: flex;
flex-direction: column;
margin: 16px 8px 8px 8px;
`;
const Input = styled.input`
border-bottom: 1px solid rgba(0, 0, 0, .23);
&:focus {
border-bottom: 1px solid #2196f3;
}
`;
class TextField extends Component {
render() {
const {
label,
req = true,
} = this.props;
return (
<Wrapper>
<Label req={req} text={label}/>
<Input type={'textfield'}/>
</Wrapper>
);
}
}
export default TextField;
Label.js
import React, {Component} from 'react';
import styled from 'styled-components';
const LabelBase = styled.label`
color: rgba(0, 0, 0, .54);
font-size: 1rem;
line-height: 1;
&:after {
content: ${props => props.req ? '*' : ''};
}
`;
class Label extends Component {
render() {
const {
req,
text,
} = this.props;
return (
<LabelBase req={req}>{text}</LabelBase>
);
}
}
export default Label;
You say you want to style the component based on the ref prop, but it seems that you're using that prop as a boolean to add text, not styles so I just went with a simplified solution for that since psuedo-selectors like :after aren't supported in React's JS styles. There are other ways around that if need be, but I think you can just do the following. However, I've included a way to pass styles to the child component as well for your reference:
class Label extends React.Component {
render() {
const {
req,
text,
moreStyles
} = this.props;
const styles = {
"color": "rgba(0, 0, 0, .54)",
"fontSize": "1rem",
"lineHeight": 1
}
return (
<div style={{...styles, ...moreStyles}}>{text + (req ? '*' : '')}</div>
);
}
}
ReactDOM.render(<Label text="test" req="Yes" moreStyles={{"backgroundColor": "blue", "border": "1px solid black"}}/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
I would like an HOC generated by styled-components to re-render when one of its properties get changed. I'm using MobX for change detection.
This doesn't respond to changes, I think I understand why. The question is if there is a simple workaround to make it work.
const DemoComponent = observer(styled.div`
background-color: ${props => props.myObject.myObservableIsTrue ? 'red' :
'green'};
`);
It's hard to tell by this little snippet, but one of my guesses would be you are not injecting any store, so currently, no store is being connected to your component.
here's a simple example of how I used styled-components with mobx if it helps:
EDITED:
I've updated the code example.
Do you know the Container / Presentational pattern?
This was the missing link.
In order to keep your renders as little as possible
you need to separate your stateful component from each other.
Spread them across a Container component (aka Dumb Components)
This way you separate state concerns and render only the component with the changed state.
UPDATED:
https://codesandbox.io/s/zxx6o2pq3l
Sorry!!! A bit of a hack job, but attempt to bring your entire code inside the #inject("store") class:
import React from "react";
import { observer, inject } from "mobx-react";
import styled, { css } from "styled-components";
#inject("store")
#observer
export default class OtherComponent extends React.Component {
render() {
const MyWrapper = (store) => {
const Wrapper = styled.div`
border: 1px solid black;
display: flex;
justify-content: space-between;
color: ${({ color }) => color};
border: 2px solid ${({ color }) => color || "black"};
padding: 10px;
margin-bottom: 10px;
`;
return (
<Wrapper {...store}>
styled-component
<button onClick={store.changeColor}>change color</button>
</Wrapper>
);
}
const { store } = this.props;
return (
<div>
{
MyWrapper(store)
}
</div>
);
}
}
Mobx is actually read like this: #inject("store") #observer export default class...
So it really an extension of an extended component; only wrapped variables will apply!
Let's say I have a tiny component like this:
Button.js
import React from 'react';
import './Button.css';
export default class Button extends React.Component {
render() {
return (
<a href={ this.props.url } className={`button button-${ this.props.type }`}>
{ this.props.content }
</a>
);
}
}
And there's some super basic styling like this:
Button.css
.button {
color: white;
padding: 1em;
border-radius: 5px;
text-decoration: none;
}
.button-primary {
background-color: red;
}
.button-primary:hover {
background-color: darkred
}
.button-secondary {
background-color: aqua;
color: black;
}
.button-secondary:hover {
background-color: darkcyan;
color: white;
}
And let's say I want to write some tests for this:
Button.test.js
import React from 'react';
import Enzyme, {shallow, mount} from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
Enzyme.configure({adapter: new Adapter()});
import Button from './Button';
import './Button.css';
// Render buttons
const primaryButton = mount(
<Button
content="Primary button"
url="http://www.amazon.co.uk"
type="primary"
/>
);
const secondaryButton = mount(
<Button
content="Secondary button"
url="http://www.ebay.co.uk"
type="secondary"
/>
);
it('should exist', () => {
expect(primaryButton).toBeDefined();
expect(secondaryButton).toBeDefined();
});
it('should display text in the button', () => {
expect(primaryButton.text()).toEqual('Primary button');
});
it('should have the correct CSS classes', () => {
expect(primaryButton.find('.button').hasClass('button-primary')).toEqual(true);
expect(secondaryButton.find('.button').hasClass('button-secondary')).toEqual(true);
});
I've set this up using react-create-app and all the above works perfectly.
My question is: how do I test that what is getting rendered looks correct? For example, in this case I would want to make sure that the buttons have the correct background colours defined in the CSS file and that they have the correct border radius. This will prevent other developers accidentally overriding critical styling for example.
I was under the impression that Enzyme did this out of the box, but I cannot understand how to interrogate the virtual DOM which I assume is happening in the background? I thought that JSDOM was automatically running and I'm executing this from the CLI which is a Node environment.
I've tried this so far:
it('should have the correct background colours', () => {
const domNode = primaryButton.find('.button').at(0).getDOMNode();
const background = getComputedStyle(domNode).getPropertyValue('background');
expect(background).toBe('red');
});
But background is returned blank, in fact if I do console.log(getComputedStyle(domNode)) I get this returned which seems to be missing the styles:
console.log src/modules/Button/Button.test.js:42
CSSStyleDeclaration {
_values: {},
_importants: {},
_length: 0,
_onChange: [Function] }
The getDOMNode of an enzyme wrapper gets you the corresponding DOM node.
You can then use getComputedStyle to get the style of that DOM:
const renderedComponent = mount(<MyComponent /);
const domNode = renderedComponent.find('div').at(0).getDOMNode();
const background = getComputedStyle(domNode).getPropertyValue('background');
expect(background).toBe('red');