Why is class at DOM not same with className at component - css

I pass className props to the child component, but sometimes in the production stage className for link active is not actually write to the DOM element, like this:
Child component (Link) (catch props className active link from parent)
Child component (Link) in DOM (client does not write class if link active)
My Code like this
Navbar
// Navbar.tsx
import classnames from 'classnames';
import { useRouter } from 'next/router';
...
const Navbar = () => {
const router = useRouter();
const navigations = [
{
href: '/',
label: 'Home'
},
{
href: '/profile',
label: 'Profile'
}
];
return (
...
{navigations.map((nav) => (
<NavLink key={nav.label} href={nav.href} isActive={router.asPath === nav.href}>
{nav.label}
</NavLink>
))}
...
);
};
export default Navbar;
Parent component
// NavLink.tsx
import classnames from 'classnames';
import Link from '#/components/elements/Link';
import styles from '#/components/parts/Navbar/styles.module.scss';
import type { NavLinkProps } from './types';
const NavLink = ({ children, href, isActive }: NavLinkProps) => (
<Link
href={href}
className={classnames(styles.navbar__link, {
[styles.navbar__link_active]: isActive
})}
>
{children}
</Link>
);
export default NavLink;
Child component
// Link.tsx
import dynamic from 'next/dynamic';
import type { LinkProps } from './types';
const NextLink = dynamic(() => import('next/link'));
const Link = ({ children, href, target, isExternal, className, label, role }: LinkProps) => {
if (isExternal) {
return (
<a
href={href}
target={target ?? '_blank'}
rel="noopener noreferrer"
className={className}
aria-label={label}
role={role}
>
{children}
</a>
);
}
return (
<NextLink href={href}>
<a target={target ?? '_self'} className={className} aria-label={label} role={role}>
{children}
</a>
</NextLink>
);
};
export default Link;

Try using template literals:
<Link className={`${classnames(styles.navbar__link, {
[styles.navbar__link_active]: isActive
}}`}>

Related

Possible to style button according to if the state value is true or false?

I have two buttons that show two different components when toggling them. For UX reasons (to know which component is showing) I would like to style the buttons according to if the value of the state is true or false (give them an underline and a darker color if the state is true). Is this possible in any way?
This is my GitHub repo: https://github.com/uohman/Portfolio2022
And this is the component where I handle the buttons:
`
import React, { useState } from 'react'
import ReactDOM from 'react-dom';
import { Subheading } from 'GlobalStyles';
import { FrontendProjects } from './FrontendProjects'
import { GraphicDesignProjects } from './GraphicDesignProjects';
import 'index.css'
export const FeaturedProjects = () => {
const [buttons, setButtons] = useState([
{ label: 'Development', value: true },
{ label: 'Graphic design', value: false }
]);
const handleButtonsChange = () => (label) => {
const newButtonsState = buttons.map((button) => {
if (button.label === label) {
return (button = { label: button.label, value: true });
}
return {
label: button.label,
value: false
};
});
setButtons(newButtonsState);
};
return (
<>
<Subheading><span>Featured projects</span></Subheading>
<SpecialButton {...{ buttons, setButtons, handleButtonsChange }} />
{buttons[0].value && <FrontendProjects />}
{buttons[1].value && <GraphicDesignProjects />}
</>
);
};
const SpecialButton = ({ buttons, setButtons, handleButtonsChange }) => {
return (
<div className="button-container">
{buttons.map((button, index) => (
<button
key={`${button.label}-${index}`}
onClick={() => handleButtonsChange({ buttons, setButtons })(button.label)}>
{button.label.toUpperCase()}
</button>
))}
</div>
);
};
const rootElement = document.getElementById('root');
ReactDOM.render(<FeaturedProjects />, rootElement);
`
I've given the buttons the pseudo element :focus and that nearly solves my problem, but still as a default the buttons are the same color although it is one of the components that is showing. Thankful for suggestions on how to solve this!
You can provide a style props to any html component.
You should pass an object where attributes are camelcased.
<button
style={{ // double bracket to pass an object
backgroundColor: yourVariable ? 'red' : undefined // notice css background-color became backgroundColor
}}
>
{button.label.toUpperCase()}
</button>
You can do the same with classes
<button
className={yourVariable && "yourClass"}
>
{button.label.toUpperCase()}
</button>
You can set styles for button based on a condition.
In this use case, you already have the state button.value which can be used as a condition to set inline styles (or classes) for the mapped button.
Example:
const SpecialButton = ({ buttons, setButtons, handleButtonsChange }) => {
return (
<div className="button-container">
{buttons.map((button, index) => (
<button
key={`${button.label}-${index}`}
// ๐Ÿ‘‡ This property is added
style={{
backgroundColor: button.value ? "#aaa" : "#eee",
textDecoration: button.value ? "underline" : "none",
}}
onClick={() =>
handleButtonsChange({ buttons, setButtons })(button.label)
}
>
{button.label.toUpperCase()}
</button>
))}
</div>
);
};
The buttons are set to become darker when selected in the above example, but you can further customize the styles for the desired result.
More about inline styles
On a side note, it is not necessary to pass state values to the the event by onClick={() => handleButtonsChange({ buttons, setButtons })(button.label)}.
The parent component always have these values, so you do not need to pass it down to SpecialButton and pass it back.
Hope this will help!
Full example:
import React, { useState } from "react";
import ReactDOM from "react-dom";
import { Subheading } from "GlobalStyles";
import { FrontendProjects } from "./FrontendProjects";
import { GraphicDesignProjects } from "./GraphicDesignProjects";
import "index.css";
export const FeaturedProjects = () => {
const [buttons, setButtons] = useState([
{ label: "Development", value: true },
{ label: "Graphic design", value: false },
]);
const handleButtonsChange = (label) => {
const newButtonsState = buttons.map((button) => {
if (button.label === label) {
return (button = { label: button.label, value: true });
}
return {
label: button.label,
value: false,
};
});
setButtons(newButtonsState);
};
return (
<>
<Subheading>
<span>Featured projects</span>
</Subheading>
<SpecialButton {...{ buttons, handleButtonsChange }} />
{buttons[0].value && <FrontendProjects />}
{buttons[1].value && <GraphicDesignProjects />}
</>
);
};
const SpecialButton = ({ buttons, handleButtonsChange }) => {
return (
<div className="button-container">
{buttons.map((button, index) => (
<button
key={`${button.label}-${index}`}
// ๐Ÿ‘‡ This property is added
style={{
backgroundColor: button.value ? "#aaa" : "#eee",
textDecoration: button.value ? "underline" : "none",
}}
onClick={() =>
handleButtonsChange(button.label)
}
>
{button.label.toUpperCase()}
</button>
))}
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<FeaturedProjects />, rootElement);

How to pass props into a Stencil child component in Storybook?

I have two Stencil components. MyCard and MyButton.
MyButton.tsx
import { Component, h, Prop } from '#stencil/core';
#Component({
tag: 'my-button',
styleUrl: 'my-button.css',
shadow: true,
})
export class MyButton {
#Prop() label: string;
render() {
return (
<div>
<button>
{this.label}
</button>
</div>
);
}
}
MyButton.stories.ts
const Template = (args) => `<my-button label="${args.label}"></my-button>`;
export const Example = Template.bind({});
Example.args = {
label: 'my custom button'
}
MyCard.tsx
import { Component, h, Prop } from '#stencil/core';
#Component({
tag: 'my-card',
styleUrls: ['my-card.scss'],
shadow: true,
})
export class MyCard {
render() {
return (
<div>
<div>
<my-button></my-button>
</div>
</div>
)
}
}
MyCard.stories.ts
const MyCardDefault = (args) => (
`<my-card>
<my-button label="${args.label}"></my-button>
</my-card>`
)
export const CardExample = MyCardDefault.bind({});
CardExample.args = {
label: "CardButton"
}
If I open the MyButton component in Storybook, the args are properly passed, and the label is applied to the button, but when I open the MyCard component, the button innerHTML is empty. The props are not passed. Why? How do I pass props to a nested child component in Stencil?
I don't know Storybook, but your MyCard component does not have any properties and does not set any properties on it's my-button element, so binding a label to the card won't do anything because MyCard doesn't use the label. I think you need:
export class MyCard {
#Prop() label: string;
render() {
return (
<div>
<div>
<my-button label={this.label}></my-button>
</div>
</div>
)
}
}

Change the style of the item in rectjs and remove it if I clicked another item

I wanted to change the style of an item with one click in a list and remove it if I click another item
I did like this but when I click on the second it doesnโ€™t change to the first
const ref = useRef();
const handleClick = () => {
if (ref.current.style.backgroundColor) {
ref.current.style.backgroundColor = '';
ref.current.style.color = '';
} else {
ref.current.style.backgroundColor = 'green';
ref.current.style.color = 'white';
}
};
<Card ref={ref} elevation={6} style={{ marginBottom: "5px"}} onClick={()=>{ handleClick()}} >
<CardContent style={{ height: "10px" }}>
<Typography >
{user}
</Typography>
</CardContent>
</Card>
);
};
any help please!
I like using a package like classnames ( yarn add classnames or npm i classnames) to apply conditional styling through classes rather than having to inline the element's styling directly.
You could pass the selected attribute to your Card component using React's useState (or Redux) and then apply conditional styling to selected cards (i.e. <Card selected />).
Component.js
import { useState } from 'react';
import { useSelector } from 'react-redux';
import Card from './Card';
const Component = () => {
const [selectedId, setSelectedId] = useState(null);
const items = useSelector(state => state.items);
const handleClick = (id) => {
setSelectedId(id);
};
return items.map(({id}) =>
<Card
key={id}
onClick={() => handleClick(id)}
selected={id === selectedId}
>
...
</Card>
);
};
export default Component;
Card.js
import { classNames } from 'classnames';
const Card = ({ children, onClick, selected = false }) => (
<div
className={
classNames('Card', {
'Card--Selected': selected
})
}
onClick={onClick}
>
{ children }
</div>
);
export default Card;
Card.scss
.Card {
// Card styling...
&--Selected {
// Selected card styling...
}
}

React transition inline style won't apply

I am working with custom Transition component and my inline styling just wont apply. I tried everything what is in my power and just got myself angry. Can somebody explain why this doesn't work.
Modal show itself on initial load and by clicking on button for show or not show this doesn't react. I mean state updates correctly (this console.log shows 0 and 1 when it should) but styling just wont apply.
import classes from "./App.module.css";
import ReactDOM from "react-dom";
import React from "react";
import Modal from "./components/Modal/Modal";
import List from "./components/List/List";
import Backdrop from "./components/Backdrop/Backdrop";
import { useState } from "react";
import Transition from "react-transition-group/cjs/Transition";
function App() {
const [modalOpen, setModalOpen] = useState(false);
const onOpenModalHandler = () => {
setModalOpen(true);
};
const onCloseModalHandler = () => {
setModalOpen(false);
};
return (
<div className={classes["App"]}>
<h1>React Animations</h1>
<Transition in={modalOpen} timeout={1000} onMountEnter onMountExit>
{(state) => {
console.log(state);
console.log(state === "exiting" || state === "exited" ? 0 : 1);
return ReactDOM.createPortal(
<Modal
closeModal={onCloseModalHandler}
modalState={modalOpen}
style={{
transition: `opacity 300ms ease-out`,
opacity: state === "exiting" || state === "exited" ? 0 : 1,
}}
/>,
document.getElementById("modal")
);
}}
</Transition>
{modalOpen &&
ReactDOM.createPortal(
<Backdrop />,
document.getElementById("backdrop")
)}
<button className={"Button"} onClick={onOpenModalHandler}>
Open Modal
</button>
<h3>Animating Lists</h3>
<List />
</div>
);
}
export default App;
Modal
import classes from "./Modal.module.css";
const Modal = (props) => {
const classNames = props.modalState
? `${classes["modal-open"]} ${classes["Modal"]}`
: `${classes["modal-close"]} ${classes["Modal"]}`;
const onCloseModalHandler = () => {
props.closeModal();
};
return (
<div className={classNames}>
<h1>A Modal</h1>
<button className="Button" onClick={onCloseModalHandler}>
Dismiss
</button>
</div>
);
};
export default Modal;

Story with its own states and methods

To demonstrate a component I try to write custom state and methods for a story.
For example, I have a component ListItems who accepts an array of string as Input.
In the story of this component I want to show an interactive example of the usage of this component.
So my story will have internal state "items" and internal method "addItem"
I know how to do that with React, but I'm stuck with Angular.
Here is a React way to do that:
(View in codesandbox)
// ListItems.tsx
import React from "react";
export type ListItemsProps = { items: string[] };
export const ListItems = ({ items = [] }: ListItemsProps) => {
return (
<ul>
{items.map((item, key) => (
<li key={key}>{item}</li>
))}
</ul>
);
};
// ListItems.stories.tsx
import React, { useState } from "react";
import { ListItems } from "./ListItems";
export default {
title: "ListItems",
component: ListItems
};
export const Text = () => {
const [items, setItems] = useState(["Demo Item"]);
const [value, setValue] = useState("");
const addItem = () => {
setItems([...items, value]);
setValue("");
};
return (
<div>
<ListItems items={items} />
<input value={value} onChange={(e) => setValue(e.target.value)} />
<input type="submit" onClick={addItem} value="add" />
</div>
);
};
};
How can I write the same story with following Angular Component ?
import { Component, OnInit, Input } from "#angular/core";
#Component({
selector: "app-list-items",
template: `<ul>
<li *ngFor="let item of items">{{ item }}</li>
</ul>`
})
export default class ListItems implements OnInit {
#Input() items: string[] = [];
constructor(){}
ngOnInit(): void {}
}
Finally I found a solution on Angular, but it's not an elegant one...
Maybe someone know a better way !
And I can't find a solution to show the code story template on "Show code" feature.
import { Story, Meta } from '#storybook/angular/types-6-0';
import { ListItemsComponent } from './list-items.component';
import { Component } from '#angular/core';
export default {
title: 'Demo/ListItems',
component: ListItemsComponent,
} as Meta;
const Template: Story<ListItemsComponent> = (args: ListItemsComponent) => ({
props:args,
});
export const BasicDemo = Template.bind({})
BasicDemo.args={
items: ["Basic Demo", "Without interaction"]
}
// Create a dedicated component for the interactive story
#Component({
selector: 'story-list-items',
template: `
<core-list-items [items]="items"></core-list-items>
<input type="text" [(ngModel)]="value" /><button (click)="addItem()">add</button>
`,
})
class InteractiveDemoComponent{
items = [];
value: string = '';
addItem(){
this.items = [...this.items, this.value];
this.value = ""
}
}
const InteractiveTemplate: Story<ListItemsComponent> = (args: ListItemsComponent) => ({
props:args,
component: InteractiveDemoComponent,
});
export const InteractiveDemo = InteractiveTemplate.bind({});
InteractiveDemo.args = {
items: ["Interactive Demo"]
}

Resources