Keep accordion items open when clicked on another - javascript

So I'm having this accordion component, which currently opens and close on click. But when I click on e.g. first item and it opens, when I click on second item the first close while I want to keep it open. Not sure how should I approach this.
const [activeTab, setActiveTab] = React.useState(props.tabIndex);
const tabs = props.tabs.map((tab) => {
return (
<>
<TabContainer>
<Tab
key={tab.index}
onClick= {() => {activTab == tab.index ? setActiveTab(-1) : setActiveTab(tab.index)})
className={typeof tab.content === 'string' ? '' : 'unactive-tab'}
>
{tab.name}
</Tab>
</TabContainer>
{tab.index === activeTab ?
<div
id="content"
style={{ width: '100%', margin: '2rem 0' }}
dangerouslySetInnerHTML={{ __html: tab.content as string }}
/>
: null}
</>
);
});

The best approach is to separate the Accordion item in its own component based on what you want and each of them would be able to have a "opened" state.
I won't give you a full solution, but I would do something similar to this:
const AccordionItem = (props) => {
// Each item has it's own state.
const [isOpened, setIsOpened] = React.useState(false);
return (YOUR COMPONENT);
};
And in your main component, you just do this:
const tabs = props.tabz.map((tab) => {
return (
<AccordionItem {...tab} /≥
);
});

change props.tabIndex to be an array of tabs indexes rather than just 1 index.
Assuming you now send an array of indexes through props. e.g
// props.tabIndexes = [1,5,2]
const [activeTabs, setActiveTabs] = React.useState(props.tabIndexes);
const tabs = props.tabs.map((tab) => {
return (
<>
<TabContainer>
<Tab
key={tab.index}
onClick= {() => {activeTabs.includes(tab.index) ? setActiveTabs(activeTabs.filter(tabIndex => tabIndex !== tab.index ) ) : setActiveTabs(activeTabs.push(tab.index)})
className={typeof tab.content === 'string' ? '' : 'unactive-tab'}
>
{tab.name}
</Tab>
</TabContainer>
{activeTabs.includes(tab.index) ?
<div
id="content"
style={{ width: '100%', margin: '2rem 0' }}
dangerouslySetInnerHTML={{ __html: tab.content as string }}
/>
: null}
</>
);
});

Related

Need a active class on first div

I'm trying to add an active class on the map element with hover. Everything is perfect but I need to add an active class on the first div when I do not hover over any.
Here is my code...
{WhatWeOfferData.map(({ img, title, para}, index) => {
return (
<div
className={`${style.card} ${addActive.index === index && addActive.show ? `${style.active}` : ""}`}
onMouseEnter={hoverOn}
onMouseLeave={hoverOff}
key={index}
data-id={index}
>
<Image src={img} alt="offer_images" width={37} height={41} />
<h2>{title}</h2>
<p>{para}</p>
</div>
);
})}
and
let [addActive, setAddActive] = useState(false);
const hoverOn = (e) => {
setAddActive({
index: parseInt(e.currentTarget.dataset.id),
show: true
});
};
const hoverOff = (e) => {
setAddActive({
index: parseInt(e.currentTarget.dataset.id),
show: false
});
};
Simply do this:
{
WhatWeOfferData.map(({ img, title, para}, index) => {
return (
<div className={`${style.card} ${addActive.index === index && addActive.show ? `${style.active}` : index === 0 && !addActive.show ? `${style.active}` : ""}`}
onMouseEnter={hoverOn}
onMouseLeave={hoverOff}
key={index}
data-id={index}
>
<Image src={img} alt="offer_images" width={37} height={41} />
<h2>{title}</h2>
<p>{para}</p>
</div>
);
})}

On toggle every array is getting open in react js

export const MobileFAQ = ({ faqs = [], defaultOpen = false }) => {
const { isOn: open, toggle } = useToggle(defaultOpen);
const ToggleIcon = open ? MinusIcon24Gray : PlusIcon24Gray;
return (
<div className="p16">
<h4 className="section-text-5 mb16">Frequently asked questions</h4>
{faqs.map((u, index) => (
<>
<div className="faq-item" onClick={toggle}>
<span className="c-black-3 text-medium">{u.question}</span>
<div className="faq-toggle-icon-mobile"></div>
</div>
<Collapse in={open}>
<div className="faq-answer c-black-3 text-4">{u.ans}</div>
</Collapse>
{index !== faqs.length - 1 && (
<div
style={{ height: 1, width: '100%', backgroundColor: '#e6e6e6' }}
className="mt12 mb16"
/>
)}
</>
))}
</div>
);
};
I have created faq array which is showing question and answer on toggle but it get open every index which should be coming as index wise

Jsx Component not showing after conditional checks

The problem is I have two redux stores
items(Items Store
quotationItems(Quote Items.
When a product item is added to quotationItems I Would to show <RedButton title="Remove" /> or when the quotationItems is empty the <AddButton title="Add" /> should be shown.
using if statement tends to re-render the component and adds new components e.g: After adding a new product to quotation items, there will be a new <AddButton /> in FlatList Component.
interface IProps {
items: ItemInterface[];
documentItems: ItemInterface[];
onAddItem: any;
}
const ItemFlatList2: FC<Partial<IProps>> = ({
items,
documentItems,
onAddItem,
}) => {
const TestView = (itemCode) => {
documentItems.map((x) => {});
};
return (
<FlatList
data={items}
keyExtractor={(item) => item.itemCode.toString()}
renderItem={({
item: { itemCode, itemDescription, itemSellingPrice },
}) => {
return (
<div className={styles.itemContainer}>
<h4>Item Code: {itemCode}</h4>
<h4>Description: {itemDescription}</h4>
<div>
{documentItems.map((x) => {
x.itemCode === itemCode ? (
<RedButton title={"Remove"} key={itemCode} />
) : (
<AddButton title={"Add"} key={itemCode} />
);
})}
</div>
</div>
);
}}
/>
);
};
Other options I've tried:
I've also tried this, still doesn't work, the buttons will show up but after clicking on the first button, which will successful add the product item to quotation item store but when I try and click on the 2nd or 3rd button of `<AddButton title={'Add'} key={itemCode} /> I get no response.
<div>
{documentItems.length <= 0 ? (
<AddButton
title={"Add"}
key={itemCode}
onClick={() => onAddItem(itemCode)}
/>
) : (
documentItems!.map((x) =>
x.itemCode === itemCode ? (
<RedButton title={"Remove"} key={itemCode} />
) : (
<
AddButton title={"Add"} key={itemCode} onClick={() => onAddItem(itemCode)} />
)
)
)}
</div>
This is the result of the modified code above:
Please add the return keyword.
documentItems.map((x) => {
return (x.itemCode === itemCode ? (
<RedButton title={"Remove"} key={itemCode} />
) : (
<AddButton title={"Add"} key={itemCode} />
);
)})
Or Just remove the curly braces.
documentItems.map((x) => (x.itemCode === itemCode ? (
<RedButton title={"Remove"} key={itemCode} />
) : (
<AddButton title={"Add"} key={itemCode} />
)))
Based on the updations that you did in the questions. It is an issue with the data in "documentItems". Since you only need 1 button per item code. Instead of map use find. It will return 1 item and fix this duplicacy.

Algolia hierarchical Menu issue

I have been using Algolia for React and I am having issues with its hierarchical Menu. When I select the third level of category from the menu everything else disappears from the screen and only the category and its parent category remains there.
Before clicking on the third level category
After clicking on the third level category. ( All other categories are gone )
This is my code for getting the categories and showing them on screen.
const HierarchicalMenu = ({ items, refine, createURL }) => {
const classes = useStyles();
console.log(items);
return (
<ul style={{ marginBottom: 0, paddingLeft: 20, marginLeft: 0 }}>
{items.map((item) => {
console.log(item.label);
console.log(item?.items ?? []);
return (
<li className={classes.categoryTitle} key={item.label}>
<a
href={createURL(item.value)}
className={cx(classes.categoryItem, item.isRefined ? 'selected' : null)}
onClick={(event) => {
event.preventDefault();
refine(item.value);
}}
>
{item.isRefined && item.items && <ArrowDropUpIcon />}
{!item.isRefined && <ArrowDropDownIcon />}
{item.label} <span>({item.count})</span>
</a>
{item.items && (
<HierarchicalMenu items={item.items} refine={refine} createURL={createURL} />
)}
</li>
);
})}
</ul>
);
};
const CustomHierarchicalMenu = connectHierarchicalMenu(HierarchicalMenu);
const CategoryFilter = () => {
return (
<CustomHierarchicalMenu attributes={['categories.lvl0', 'categories.lvl1', 'categories.lvl2']} />
)
}
This is how I have created the categories node in Alglolia

Apply style on specific react map item

I would like to apply a class on a specific element of loop map.
const items = [
{
key: 'Menu1key',
content: 'Menu 1',
},
{
key: 'Menu2',
content: 'Menu2key'
},
{
key: 'Menu3Key',
content: 'Menu3',
children: [{
key: 'SubMenu3Key',
content: 'SubMenu3',
},
{
key: 'SubMenu5Key',
content: 'SubMenu5'
}]
},
{
key: 'Menu4Key',
content: 'Meu4',
children: [{
key: 'SubMenu4Key',
content: 'SubMenu4',
},
{
key: 'SubMenu5Key',
content: 'SubMenu5'
}]
}
]
const StackedMenu = (props) => {
let [chevronRotation, setChevronRotation] = useState('')
let [itemClicked, setItemClicked] = useState(props.itemClicked);
let [depth, setDepth] = useState(props.depth)
return (<Menu primary defaultActiveIndex={0} activeIndex={itemClicked} vertical pointing style={{ width: '100%' }}>
{props.items.map((item, index) => {
return (<>
<MenuItem onClick={(e, data) => { setItemClicked(data.index); setChevronRotation(`rotate`) }} index={index} key={index} pointing="start" >
<Menu.ItemContent style={depth > 0 ? { paddingLeft: '0.5em' } : null} >{item.content}</Menu.ItemContent>
{item.children ?
(<Menu.ItemIcon id={index} style={{ right: "0px", position: "absolute" }} className={props.itemClicked === index && item.children ? 'rotate' : ''}>
<ChevronEndMediumIcon />
</Menu.ItemIcon>) : null}
</MenuItem>
{itemClicked === index && item.children ? <div><StackedMenu items={item.children} depth={depth + 1} itemClicked={0} /></div> : null}
</>)
})}
</Menu>)
}
I using a recursive component to create children sub menu.
Actually when I open a menu all chevrons get expanded , what I'm looking for is that only the chevron of the clicked item get expanded.
Your problem is that you're setting a local state to control whether it should be open, but then checks on the props for the clicked item.
Change this line:
// I removed props from props.itemClicked
<Menu.ItemIcon id={index} style={{ right: "0px", position: "absolute" }} className={itemClicked === index && item.children ? 'rotate' : ''}>
You should use the component's state to check if menu item is opened or not.
Also, I've removed unnecessary depth state. Hope this could help.
const StackedMenu = ({items, depth, ...otherProps}) => {
let [chevronRotation, setChevronRotation] = useState('');
let [itemClicked, setItemClicked] = useState(otherProps.itemClicked);
return (
<Menu
primary
defaultActiveIndex={0}
activeIndex={itemClicked}
vertical
pointing
style={{width: '100%'}}>
{items.map((item, index) => {
return (
<>
<MenuItem
onClick={(e, data) => {
setItemClicked(data.index);
setChevronRotation(`rotate`);
}}
index={index}
key={index}
pointing="start">
<Menu.ItemContent
style={depth > 0 ? {paddingLeft: '0.5em'} : null}>
{item.content}
</Menu.ItemContent>
{item.children ? (
<Menu.ItemIcon
id={index}
style={{right: '0px', position: 'absolute'}}
className={itemClicked === index ? 'rotate' : ''}>
<ChevronEndMediumIcon />
</Menu.ItemIcon>
) : null}
</MenuItem>
{itemClicked === index && item.children ? (
<div>
<StackedMenu
items={item.children}
depth={depth + 1}
itemClicked={0}
/>
</div>
) : null}
</>
);
})}
</Menu>
);
};

Categories

Resources