How to create a component like Bootstrap accordion? - reactjs

I want a component to behave exactly like Accordion.
I have managed to do that.
But I am facing one issue.
If one question is open and a user tries to open the other question,it closes the first question.
I do not want that.
I want both of them to remain open and it should be closed if a user clicks on the question again.
Here is the code -->
class Accordion extends React.Component {
constructor() {
super();
this.state = {
answer: false,
selectedQue: null,
};
}
toggleAnswer = (index) => {
const { answer } = this.state;
this.setState({
selectedQue: index,
answer: !answer,
});
};
render() {
const { answer, selectedQue } = this.state;
return (
<Row className="m-t-10">
<Col md={12} sm={12} xs={12}>
{map(
questionAnswer,
(item, index) =>
item && (
<div key={index} onClick={() => this.toggleAnswer(index)}>
<div className="d-flex justify-content-between">
<Label
header={item.key}
color={index === selectedQue && answer ? '#406AE8' : '#415070'}
/>
<span className="float-right">
<Icons
className={cx(
'vertical-align-middle',
index === selectedQue && answer
? 'ac-icon-arrow-up-xs'
: 'ac-icon-arrow-down-xs',
)}
/>
</span>
</div>
{index === selectedQue && answer && (
<div>
<span
dangerouslySetInnerHTML={{
__html: item.value,
}}
/>
</div>
)}
<hr />
</div>
),
)}
</Col>
</Row>
)
}
}

Related

How can I add item on localStroge through ReactJS

I don't understand how I can add an item on localStroge with the handle change method in react js.
The problem is I want to add a favorite list. When I click the favorite list all checkbox fill I need to control them.
I want to store the filled item in my local storage. handlechange function fill all favorite icon why?
Every click will be a single item fill. Is it possible to Material UI or Martial UI checkBox icon? How can I handle it?
Here is my UI view
function Main() {
// Here is my state define
const [checked, setChecked] = React.useState(
localStorage.getItem("love") === "true"
);
const handleChange = (e) => {
localStorage.setItem("love", `${e.target.checked}`);
setChecked(e.target.checked);
console.log(e.target.checked);
};
return (
<>
<div className="row mt-5">
{isLoading ? (
<>
{Array.from({ length }).map((_, i) => (
<MainLoading key={i} />
))}
</>
) : error ? (
<p>Error occured</p>
) : (
<>
{data?.map((product) => (
<div className="col-md-3 mb-5 text-center" key={product.id}>
<img
className="w-100"
style={{ height: "200px", objectFit: "contain" }}
src={product.image}
alt=""
/>
<div>{product.title.substring(0, 20)}</div>
<button
onClick={() => handelAddTocard(product)}
className="mt-3"
>
Add to card
</button>
<button>
<Link
to={`/details/${product.id}`}
className="mt-3 text-decoration-none text-black"
>
view details
</Link>
</button>
{/* How can i control evey single item */}
<Checkbox
checked={checked}
onChange={handleChange}
icon={<FavoriteBorder />}
checkedIcon={<Favorite />}
/>
</div>
))}
</>
)}
</div>
</>
);
}
export default Main;
The problem is that you are using a boolean and you have no way to identify a specific item.
If you want to favorite multiple items, I would use something like this:
const [checked, setChecked] = React.useState(
JSON.parse(localStorage.getItem("loveIds") || "[]")
);
const handleCheck = (id, productChecked) => {
const newItems = productChecked ? [...checked, id] : checked.filter(x => x !== id);
localStorage.setItem("loveIds", JSON.stringify(newItemS));
setChecked(newItems);
console.log(newItems);
};
// ...
<Checkbox
checked={checked}
onChange={(e) => handleCheck(product.id, e.target.checked)}
icon={<FavoriteBorder />}
checkedIcon={<Favorite />}
/>

How to open up only 1 panel onclick on React?

I have a button in each of the 3 panels. I am looking at a dropdown message in that one panel where I clicked the button. But currently, when I click on one of the buttons, all 3 panels will show the dropdown message. I read about making use of indexing but I am not exactly sure how to add it in. How can I go about solving this?
export default class CustomerDetails extends Component {
constructor(props) {
super(props);
this.state = {
listOpen: false,
};
}
// Toggle the dropdown menu
toggleList(name) {
this.setState(prevState => ({
listOpen: !prevState.listOpen
}))
}
render() {
const { listOpen } = this.state
if (!this.state.customerDetails)
return (<p>Loading Data</p>)
return (<div className="customerdetails">
<div className="addmargin">
<div className="col-md-9">
{this.state.customerDetails.data.map(customer => (
<Panel bsStyle="info" key={customer.name}>
<Panel.Heading>
<Panel.Title componentClass="h3">{customer.name}</Panel.Title>
</Panel.Heading>
<Panel.Body>
<img src={require(`./sampleimages/${customer.image}.jpg`)} className="Customer-image" alt="image" />
<br line-height="110%"></br>
<p align="left">{customer.desc}</p>
{/* Toggle dropdown menu */}
<div className="image-cropper">
<button><img src={arrow} className="arrow-button" onClick={() => this.toggleList(customer.name)} /></button>
{listOpen && <ul className="dd-list">
<li class="dropdown" className="dd-list-item" key={customer.name}>{customer.tip1}</li>
</ul>}
</div>
You can do it like this. In your state declare a variable which points to index of the panel you want to show as:
this.state = {
listOpen: 0,
};
Then modify your toogleList method as:
toggleList(index){
this.setState({ listOpen: index })
}
And finally, change your JSX as:
{this.state.customerDetails.data.map((customer, index) => (
<Panel bsStyle="info" key={customer.name}>
<Panel.Heading>
<Panel.Title componentClass="h3">{customer.name}</Panel.Title>
</Panel.Heading>
<Panel.Body>
<img src={require(`./sampleimages/${customer.image}.jpg`)} className="Customer-image" alt="image" />
<br line-height="110%"></br>
<p align="left">{customer.desc}</p>
{/* Toggle dropdown menu */}
<div className="image-cropper">
<button><img src={arrow} className="arrow-button" onClick={() => this.toggleList(index)} /></button>
{listOpen === index && <ul className="dd-list">
<li class="dropdown" className="dd-list-item" key={customer.name}>{customer.tip1}</li>
</ul>}
</div>
</PanelBody>
<Panel>
}
Hope this works for you.
// Toggle the dropdown menu
toggleList(index) {
let customerDetails = this.state.customerDetails
if (customerDetails.data[index].listOpen)
customerDetails.data[index].listOpen = false
else
customerDetails.data[index].listOpen = true
this.setState({ customerDetails })
}
change this function like this
{
this.state.customerDetails.data.map((customer, index) => (
<Panel bsStyle="info" key={customer.name}>
<Panel.Heading>
<Panel.Title componentClass="h3">{customer.name}</Panel.Title>
</Panel.Heading>
<Panel.Body>
<img src={require(`./sampleimages/${customer.image}.jpg`)} className="Customer-image" alt="image" />
<br line-height="110%"></br>
<p align="left">{customer.desc}</p>
{/* Toggle dropdown menu */}
<div className="image-cropper">
<button><img src={arrow} className="arrow-button" onClick={() => this.toggleList(index)} /></button>
{customer.listOpen && <ul className="dd-list">
<li class="dropdown" className="dd-list-item" key={customer.name}>{customer.tip1}</li>
</ul>}
</div>

How to delete an item on the front end from a list of items in ReactJS

I'm trying to delete an item from a list of items that I get dynamically from a REST API. For some reason, my onDelete function is being called when I press the Search button, instead of individually when I want to delete a specific list item. Not sure why.
class Users extends Component {
constructor(props) {
super(props);
this.state = {
searchValue: '',
users: []
};
}
handleOnChange = event => {
this.setState({ searchValue: event.target.value });
};
handleSearch = () => {
this.makeApiCall(this.state.searchValue);
};
onDelete(e) {
console.log('why is this being called?');
}
makeApiCall = async searchInput => {
let res = await axios(
`https://zuul-stage.whatifops.com/v1/user/phone/${searchInput}`
);
this.setState({ users: res.data });
};
render() {
return (
<div>
<input
name='text'
type='text'
placeholder='Search'
onChange={event => this.handleOnChange(event)}
value={this.state.searchValue}
/>
<button onClick={this.handleSearch}>Search</button>{' '}
{this.state.users ? (
<div>
{this.state.users.map((user, index) => (
<div key={user.id}>
<Row>
<Col lg={2} style={{ maxWidth: '9.7%' }}>
<Button
color='danger'
style={{ paddingTop: 12, paddingBottom: 12 }}
onClick={this.onDelete()}
>
<i className='fa fa-trash'></i> Delete
</Button>
</Col>
<Col lg={10}>
<ListGroup>
<ListGroupItem>
<strong>Email:</strong> {user.email}
<strong>Phone:</strong> {user.phone}
</ListGroupItem>
</ListGroup>
</Col>
</Row>
</div>
))}
</div>
) : (
<p>Try searching for a user</p>
)}
</div>
);
}
}
export default Users;
The onDelete function I was using was
onDelete(e){
let id = e.target.id;
let updatedUsers = this.users.filter(user=>user.id!=id)
this.setState({users:updatedUsers })
}
but I was getting an error about the users being undefined, and it was not being called individually onClick. Not sure what I am doing wrong, I thought this would be a simple thing to build but I'm struggling!
The issue is that the onDelete is being called (will get called automatically unless the following is changed)
change:
{this.onDelete()}
to:
{() => this.onDelete()}
or to (once onDelete is bounded correctly):
{this.onDelete}

React/Mobx: Adding checkbox handler breaks a textarea field

I recently added a few checkboxes to a component. In the map function below, 5 of them are rendered.
After attaching a checkbox onchange handler, not only did the check/uncheck action fail to implement, but editing the text of a textarea in this component now breaks the editing function and causes an infinite loop in MobX (in the console).
However, removing the onChange handler from the checkboxes immediately fixes the issue with the textarea.
I don't see how the 2 are related, does anyone know what is happening? Here is the component:
const FinalEditsAndCopy = (props) => {
const update_final_textarea = (text_input) => {
ui_store.set_final_text_message(text_input.target.value);
console.log(text_input.target.value);
};
const render_social_media_checkboxes = () => {
return static_local_data.social_media_sites.map((social_media_site, i) => (
<List.Item key={i}>
<List.Content>
<input
type="checkbox"
checked={ui_store.final_social_media_site_selections[social_media_site]}
name={social_media_site}
className={ checkbox_style }
onChange={ ui_store.reverse_checkbox_state(social_media_site) } // This is the line that breaks the textarea and also does not fill its intended purpose
/>
{Lodash.capitalize(social_media_site)}
</List.Content>
</List.Item>
))
};
const render_social_media_checkboxes_test = () => {
return static_local_data.social_media_sites.map((social_media_site, i) => (
<List.Item key={ i }>
<List.Content>
<p>Test</p>
</List.Content>
</List.Item>
));
};
return (
<div className={ outer_container_style }>
<Container>
<Form>
<TextArea autoHeight
value={ ui_store.final_text_message }
className={ textarea_style }
onChange={ update_final_textarea }
/>
<Message attached='bottom' positive className={ alert_style }>
<Icon
name='clipboard list'
color='green'
size='big'
/>
Copied to clipboard
</Message>
</Form>
<Header size='small'>Select Social Media Sites</Header>
<div>
<List horizontal>
{render_social_media_checkboxes()}
</List>
</div>
<Button size='huge' color='orange'>Copy Text and Open Social Media Sites in New Tabs</Button>
</Container>
</div>
);
};
export default observer(FinalEditsAndCopy);
MobX observables in ui_store:
final_text_message = '';
final_social_media_site_selections = {
"facebook" : true,
"twitter" : true,
"linkedin" : true,
"instagram" : true,
"pinterest" : true
};
And the relevant actions:
set_final_text_message(input_message) {
this.final_text_message = input_message
}
reverse_checkbox_state(social_media_site) {
this.final_social_media_site_selections[social_media_site] = !this.final_social_media_site_selections[social_media_site]
}
You are invoking reverse_checkbox_state directly on render. You want to give onChange a function that will be called when the change event occurs, not invoke the function yourself.
<input
type="checkbox"
checked={ui_store.final_social_media_site_selections[social_media_site]}
name={social_media_site}
className={ checkbox_style }
onChange={() => ui_store.reverse_checkbox_state(social_media_site)}
/>

React Material UI Delete Selected Cells

I am using a table example from material ui.
This is the link to the online project https://codesandbox.io/s/209kpmpvx0
The example allows you to cell multiple rows and a delete icon gets displayed when you click on the row.
I would like to be able to print out all the selected rows after the delete icon has been pressed.
This is the class that allows you to select a row.
class EnhancedTable extends React.Component {
constructor(props) {
super(props);
this.state = {
selected: [],
}
handleClick = (event, id) => {
const { selected } = this.state;
const selectedIndex = selected.indexOf(id);
let newSelected = [];
this.setState({ selected: newSelected });
};
I put the following code selectedId={selected.id} into the EnhancedTableHead component.
return (
<Paper className={classes.root}>
<EnhancedTableToolbar numSelected={selected.length} />
<div className={classes.tableWrapper}>
<Table className={classes.table} aria-labelledby="tableTitle">
<EnhancedTableHead
numSelected={selected.length}
selectedId={selected.id}
order={order}
orderBy={orderBy}
onSelectAllClick={this.handleSelectAllClick}
onRequestSort={this.handleRequestSort}
rowCount={data.length}
/>
Then I added the selectedId to the EnhancedTableToolbar. I also added an event handler into the delete icon.
let EnhancedTableToolbar = props => {
const { numSelected, classes } = props;
return (
<Toolbar
className={classNames(classes.root, {
[classes.highlight]: numSelected > 0
})}
>
<div className={classes.title}>
{numSelected > 0 ? (
<Typography color="inherit" variant="subheading">
{numSelected} selected
</Typography>
) : (
<Typography variant="title" id="tableTitle">
Nutrition
</Typography>
)}
</div>
<div className={classes.spacer} />
<div className={classes.actions}>
{numSelected > 0 ? (
<Tooltip title="Delete">
<IconButton aria-label="Delete">
<DeleteIcon onClick={printdeletearray} />
</IconButton>
</Tooltip>
) : (
<Tooltip title="Filter list">
<IconButton aria-label="Filter list">
<FilterListIcon />
</IconButton>
</Tooltip>
)}
</div>
</Toolbar>
);
};
I get an error saying selectedId is undefined.
const printdeletearray = () => {
console.log(this.state.selected);
};
You made two mistakes I guess:
firstly you should pass selected as selectedId props in <EnhancedTableToolbar />:
<EnhancedTableToolbar
numSelected={selected.length}
selectedId={selected}
/>
the second mistake is that you should pass selectedId to printdeletearray() like this:
<DeleteIcon onClick={() => printdeletearray(selectedId)} />
and here is your Demo

Resources