I'm having a hard time understanding why this simple Bootstrap custom checkbox doesn't work. I'm using React, Bootstrap, and React-Boostrap.
import React from "react";
import ReactDOM from "react-dom";
import { Button, Form, FormCheck } from "react-bootstrap";
import "bootstrap/dist/css/bootstrap.min.css";
function App() {
return (
<div className="p-3">
<FormCheck custom label="Checkbox">
</FormCheck>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
Online Example
Adding id="test" to FormCheck seems to cause the checkbox to work, but is is possible to not have to use id? (The checkbox is used within the component of an object in my actual code, and it would be unnecessarily complicated to come up with a unique id for every checkbox)
Try this:
<FormCheck>
<FormCheck.Label>Allow us to contact you?
<FormCheck.Input isInvalid type={radio} />
</FormCheck.Label>
<Feedback type="invalid">Yo this is required</Feedback>
</FormCheck>
The basic idea is that you need to change the way it's rendered.
By default it has label and input on the same level, meaning that you have to bind them using id and for. And you want to put input inside of the label to bind it without id.
You might need some custom css as mentioned in this answer: https://stackoverflow.com/a/57901478/4536543
If you do want to use the React Bootstrap components you will have to use an ID to use the FormCheck component. This is as within the FormCheck component the ID is used to call useContext() so that the component can access the element through the DOM.
Source Code:
/** A HTML id attribute, necessary for proper form accessibility. */
id: PropTypes.string,
const { controlId } = useContext(FormContext);
const innerFormContext = useMemo(
() => ({
controlId: id || controlId,
custom,
}),
[controlId, custom, id],
);
Source
Ended up finding the solution here (Normal bootstrap 4)
<FormCheck custom>
<FormCheck.Label>
<FormCheck.Input type="checkbox" name="checkbox-name" />
<div className="custom-control-label"></div>
</FormCheck.Label>
</FormCheck>
It involves a bit of a work around, nesting labels with checkboxes. Bootstrap 4 custom buttons seem to have strange behaviour
Related
Mantine accordion specifies that its content should be only of type Accordion.Item (or, AccordionItem) - see the documentation for the children props. This means that even functions that actually return AccordionItem will not be listed.
So, this simple component will display only AccordionItem(s) that were instantiated inline, yet, not one returned from another function (see MyAccordionItem in this simple app).
The code:
import React, { Component } from 'react';
import { render } from 'react-dom';
import { Accordion } from '#mantine/core';
const MyAccordionItem = ({children, label}) =>
<Accordion.Item label={label}>
{children}
</Accordion.Item>;
function App() {
return (
<div style={{width: 200}}>
<div>pre</div>
<Accordion>
<Accordion.Item label="section0">
<div>sec0.item1</div>
<div>sec0.item2</div>
<div>sec0.item3</div>
</Accordion.Item>
{/* Section 1 is not displayed because it is no of type Accordion.Item
<MyAccordionItem label="section 1">
<div>sec1.item1</div>
<div>sec1.item2</div>
<div>sec1.item3</div>
</MyAccordionItem>
<Accordion.Item label="section2">
<div>sec2.item1</div>
<div>sec2.item2</div>
<div>sec2.item3</div>
</Accordion.Item>
</Accordion>
<div>post</div>
</div>
);
}
render(<App />, document.getElementById('root'));
The reason is the filter function defined here, and used by the accordion here.
My questions are:
Do you know of any work around that helps assigning the same type (or, appearing to be an Accordion.Item when defining your own component?
On a larger scale, What do you think is the right approach from the library's perspective (in this case mantine), i.e. should it suffice in checking that the returned type is An accordion item?
I came across this same issue and found this GitHub issue in the Mantine repository with more information about creating child components that return AccordionItems.
rtivital (Mantine's creator) states:
Yes, currently that is not supported, it will require breaking changes, so Accordion and other similar components will be Migrated to context with next major release (5.0)
As to a work around, I haven't found anything yet other than creating a custom component for the content inside the AccordionItem and then wrapping that with the Mantine AccordionItem component inline via mapping or explicitly listing them.
I have a below HTML component created in REACT which I cant edit directly. But I need to add one attribute to one of its HTML element. I can't use jquery as well other wise its easy to do with jQuery.
Below is the base HTML for table which I cant edit directly but I can just use this component in my code.
Challenge : I need to add attribute to the SVG element. e.g. -> data-id="1". Can it be done with CSS or any other way.
<TablePresenter>
<div>
<svg>this is a actually a sort button</svg>
<div>Column 1 Name</div>
</div>
<div>
<svg>this is a actually a sort button</svg>
<div>Column 2 Name</div>
</div>
</TablePresenter>
main file which I can edit is as below.
const MyComponent = () => {
some logic here...
Can we do something here may be CSS or any react hack to get underline component HTML change.
return(
<TablePresenter></TablePresenter>
)
}
export default MyComponent;
Not a recommended way of doing, but using a ref you can achieve this, a sample below:
Add a wrapper div to your parent component (your own component which can be edited) like below and add a ref to it, that will helps you to get the DOM reference of the html inside the <TablePresenter>
const divRef= useRef<HTMLDivElement>(null);
return <div ref={divRef}>
<TablePresenter></TablePresenter>
</div>
// This is an example of getting the reference of the svg.
divRef.current.firstElementChild.firstElementChild.firstElementChild
May be something like this:
const MyComponent = () => {
const divRef = useRef<HTMLDivElement>(null);
useEffect(()=>{
divRef.current.firstElementChild.firstElementChild.firstElementChild.attributes["data-id"]=1
}, [])
return <div ref={divRef}>
<TablePresenter></TablePresenter>
<SuperChild />
</div>
}
I'm trying to make a light / dark theme . And I have 2 components.
The first component is the one below. I succeeded to make a boolean to change my style in my component. It was good so far :
export interface Props{
theme:boolean;
}
const Theme: FC<Props> = ({theme}) => {
const [light,setLightMode]=useState(false);
return (
<div>
<div className="card__theme">
<div className={light ? "card__light" : "card__light--active"}>Light</div>
<Switch inputProps={{ "aria-label": "controlled" }} onChange={()=>setLightMode(!light)}/>
<div className={ light ? "card__Dark" : "card__Dark--active"}>Dark</div>
</div>
</div>
);
};
What I do right now is to use this component in my Home page where I want to do the same thing.
I want to change the style of my components , making 2 className like the code above with a boolean state.
The problem that I'm facing is that I can't make another function onChange={} in my home component. First I don't send in interface a function . I want my const [light,setLightMode]=useState(false); and my function onChange={()=>setLightMode(!light)} when I switch the button to work in my Home component from below
const Home: FC = () => {
const percentage = 91;
const [dark,setDark] = useState(false);
return (
<div>2
<div className="Container-body">
<div className="card">
<div className={dark ? "card__theme" : "card__theme--active"}>
<Theme theme={dark} onChange={()=>{setDark(!dark)}}/>
</div>
So to make it sort. I want to use just the light, setlightTheme boolean state from my Theme component in my Home component . I don't know how I can send this with props in TypeScript...
Here is a print with my code : enter link description here
I'm not sure if I understand correctly, but I think your goal is to pass the onChange function to the Theme component from the parent component.
If so, I hope this snippet can help you. Basically I added the onChange parameter in the interface as a function. I also renamed the theme parameter to dark.
To make the two components work together, I removed the useState in the Theme component and used the dark variable (taken from the parent component) to manage the styles.
export interface Props {
dark: boolean;
onChange?: () => void;
}
const Theme: FC<Props> = ({ dark, onChange }) => {
return (
<div>
<div className="card__theme">
<div className={dark ? 'card__light' : 'card__light--active'}>
Light
</div>
<Switch
inputProps={{ 'aria-label': 'controlled' }}
onChange={onChange}
/>
<div className={!dark ? 'card__Dark' : 'card__Dark--active'}>Dark</div>
</div>
</div>
);
};
So if I understand correctly you just want to provide an option for switching light and dark mode. From an architectural point of view you don't want to do this on a component-level since multiple component might need to change.
BEM Mistake
Before I address a proper solution you are currently making a BEM mistake with this line:
<div className={dark ? 'card__light' : 'card__light--active'}>
A modifier --modifier can only live together with an actual element card. So on a fully outputted HTML this is valid card__light card__light--active but in your example either card__light or card__light--active is being rendered.
Solution for theme switching
When it comes down to switching a dark/light theme you either want to make this value (true or false) available in a centralised state by using something like Redux or you might want to declare it on your root component (most often the <App />) and start using it from there.
I don't recommend prop drilling for this use case since it will make your code messy in the long run. This is a really nice use case for the Context API.
✅ See working example Playground.
P.s. I use Typescript for better code, you can ignore the syntax you don't know.
React documentation
The React documentation has an exact example for you see docs theme switching.
UPDATE
Here's are some demos
contentEditable demo - requires double click for H1 to become editable
replace with input demo - adopts event.target styles but makes the UI 'twitch' when rendered
So I have some functional components, let's say:
component1.js
import React from 'react';
const component1 = props => (
<div>
<h1>Title</h1>
</div>
);
export { component1 };
They are variable. event.target could be anything with text, so paragraph, heading, anything. I'm trying to let users edit content inline by clicking on it, so I'll pass a function editMode to these functional components, that'll update parent state with editing info, let's say like this:
<h1 onClick={event => {editMode(event, props.name, props.title, 'title')}}>title</h1>
This changes parent local state to have all the necessary information to grab the value from redux, define a target etc. For this example, props.name is the name of the component, props.title is the value, and 'title' is object key in redux.
So I'll add something to my component1.js and make it look a bit like this:
import React from 'react';
const component1 = props => (
<div>
{props.editState === 'true' &&
<EditLayout
name={props.name}
target={props.target}
value={props.value}
onChange={event => someFunc(event)}
/>
}
<h1>Title</h1>
</div>
);
export { component1 };
Now this works fine, except it doesn't scale. EditLayout, in this case, will just return an input with correct value. What I need it to do is to adapt to whatever is being clicked, get font size, background, padding, margin, position. Am I doing this right? Every way I try, I run into huge issues:
Idea 1 - move EditLayout component outside of the functional component
Issue: positioning
So I'll move EditLayout to parent component that contains both component1.js and EditLayout. This will allow me to manipulate it from inside the functional component, without having to include it everywhere. I'll then grab coordinates and other important information from event.target like so:
const coords = event.target.getBoundingClientRect();
const offsetX = coords.left;
const offsetY = coords.top;
const childHeight = coords.height;
const childWidth = coords.width;
const childClass = event.target.className;
I'll then wrap the EditLayout to return a container which contains an input, and apply size/coordinates to the absolutely positioned container. This'll present an issue of input being offset by a random amount of pixels, depending on how big/where is the event.target.
Idea 2 - pass relevant computed styles to EditLayout
Issue: twitching on render, and I have to add EditLayout for every possible event.target there is, as well as condition its' render
So I'll grab all important computed styles like this:
const computedTarget = window.getComputedStyle(event.target);
const childMargins = computedTarget.marginBottom;
const childPaddings = computedTarget.padding;
const childFontSize = computedTarget.fontSize;
const childTextAlign = computedTarget.textAlign;
And pass it to component1.js, and then pass it to EditLayout component inside the component1.js. I'll then condition theevent.target to hide if it's being edited like this:
<h1 className={ props.target === 'title' ? 'd-none' : ''}>Title</h1>
And condition the EditLayout to show only if it's needed:
{props.target === 'title' && <EditLayout />}
In this example, clicking h1 will show the input, but the layout itself with twitch on render. Input will have the exact same margin and font size as the h1, or event.target, but it'll appear bigger and extend the layout. Demo:
Idea 3 - Use conditional contentEditable
Issue: Requires double click to enable, doesn't work in safari, doesn't let me preselect the value
This is the weirdest of them all. I figured it'd be pretty simple, do something like this inside the functional component render:
<h1 contentEditable={props.target === 'title'} onClick={event => props.setTarget(event)}>Title</h1>
However, I have to double click to enable it. I have no idea why, if I attach a console log every time onClick is fired, I'll get correct outputs, I'll get the correct target value as well. I've tried numerous ways, but it simply requires double click. Even attempted to handle this inside the functional component, as most of the stuff is handled by a parent component, doesn't make a difference.
I have oversimplified the examples, so it's safe to assume/understand the following:
I am passing props in a correct fashion, they aren't undefined
I am using bootstrap
I am using styled components, and EditLayout is a styled component
which accepts props and turns them into CSS like: font-size: ${props
=> props.fontSize};
The values should be correct, I am not manipulating anything I get back from getComputedStyle() or getBoundingClientRect()
I am keen on keeping my functional components functional, and easy to
add. Functional components, in this case, are simple HTML structures,
and I'd like to keep them as simple as possible
So there's a neat solution to contentEditable requiring two clicks instead of one, instead of binding onClick and passing it to enable contentEditable, simply keep contentEditable true and handle the change however you like. Here's a working h1 that doesn't require two clicks to enable contentEditable, unlike the one in the demo
<h1
className="display-4 text-center"
contentEditable
suppressContentEditableWarning
onBlur={event => updateValues(event)}
>
Title
</h1>
The available methods for trigger update could be onBlur or onInput.
I'm learning how to use css modules with react, below is a working example of a checkbox here's what it looks like (pure HTML + CSS fiddle) without react.
import React from 'react'
import CSSModules from 'react-css-modules'
import styles from './checkbox.css'
export function Checkbox (props) {
return <div styleName="checkbox--container">
<input styleName="checkbox" type="checkbox" {...props}/>
<span styleName="checkbox--styled"></span>
</div>
}
export default CSSModules(Checkbox, styles)
This is great and all, but let's say my client comes back and wants me to change the checkbox color. Fine, seems easy enough? That fiddle above shows that there is a couple of hex-codes (#479ccf) for border and within the SVG background image. Ideally I'd be able to pass along a react prop that allows you to do something like this <Checkbox color="#666333"/> and blam! However I can't find any documentation / way to get information from the component to the CSS file.
What does this mean?
I know what the react community is going to say, that particular set of CSS isn't ideal. It can be written in javascript within the component and from there you can set the inline styles of the pieces of the checkbox to the prop color. I'm pretty sure this is possible. Is this necessary? I'd have to refactor the ::after code, question about that.
I started on making the SVG functions.
function SVGDash (color) {
return `data:image/svg+xml;charset=US-ASCII,<svg%20xmlns%3D"http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg"%20viewBox%3D"0%200%2012%2012"%20enable-background%3D"new%200%200%2012%2012"><style%20type%3D"text%2Fcss">circle%2Cellipse%2Cline%2Cpath%2Cpolygon%2Cpolyline%2Crect%2Ctext%7Bfill%3A%23${color}%20%21important%3B%20%7D<%2Fstyle><path%20d%3D"M6%200"%2F><path%20d%3D"M.8%207C.3%207%200%206.7%200%206.2v-.4c0-.5.3-.8.8-.8h10.5c.4%200%20.7.3.7.8v.5c0%20.4-.3.7-.8.7H.8z"%2F><%2Fsvg>`
}
function SVGCheck (color) {
return `data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20enable-background%3D%22new%200%200%2024%2024%22%3E%3Cstyle%20type%3D%22text%2Fcss%22%3Ecircle%2Cellipse%2Cline%2Cpath%2Cpolygon%2Cpolyline%2Crect%2Ctext%7Bfill%3A%23${color}%20%21important%3B%20%7D%3C%2Fstyle%3E%3Cpath%20d%3D%22M23.6%205L22%203.4c-.5-.4-1.2-.4-1.7%200L8.5%2015l-4.8-4.7c-.5-.4-1.2-.4-1.7%200L.3%2011.9c-.5.4-.5%201.2%200%201.6l7.3%207.1c.5.4%201.2.4%201.7%200l14.3-14c.5-.4.5-1.1%200-1.6z%22%2F%3E%3C%2Fsvg%3E`
}
You could create a different class composing the class you want to change the color:
.checkbox--styled-red {
composes: checkbox--styled;
background-image: url("data:image .... FF0000 ....");
}
And set it in the component when you get the matching props.color:
import React from 'react'
import CSSModules from 'react-css-modules'
import styles from './checkbox.css'
function getStyleNameForColor(color) {
colorToStyleName = {
red: 'checkbox--styled-red'
};
return colorToStyleName[color] || 'checkbox--styled';
}
export function Checkbox (props) {
return <div styleName="checkbox--container">
<input styleName="checkbox" type="checkbox" {...props}/>
<span styleName={getStyleNameForColor(props.color)}></span>
</div>
}
export default CSSModules(Checkbox, styles)
Better yet, use classnames instead of getStyleNameFor(color).