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
Related
How it is possible item drag and drop into inside iframe ?
const Xcomponent = () => {
return (
<>
{items && items.map( ( item, index)=> {
return(<div> {item.name} </div>)
})}
<iframe className=""drop-container></iframe>
</>
};
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
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}
</>
);
});
I have implemented a simple drag and drop code here below using react-beautiful-dnd. The frontend alone works perfectly fine, however, when I tried to connect this with the backend the items in the droppable context are unable to be dragged. That is, I am unable to drag the items within the column and also between columns. Further, I am unable to figure out how to pass the index of the elements in the mongoDb database.
The code I used is here below
projectsDashboard.js
function ProjectsDashboard() {
const handleDragEnd = ({destination, source}) => {
if (!destination) {
return
}
if (destination.index === source.index && destination.droppableId === source.droppableId) {
return
}
// Creating a copy of item before removing it from state
const itemCopy = {...state[source.droppableId].items[source.index]}
setState(prev => {
prev = {...prev}
// Remove from previous items array
prev[source.droppableId].items.splice(source.index, 1)
// Adding to new items array location
prev[destination.droppableId].items.splice(destination.index, 0, itemCopy)
return prev
})
}
const dispatch = useDispatch();
useEffect(() => {
dispatch(getStages());
},[dispatch]);
const { stage } = useSelector(state => state.stage);
var formattedArray = stage.map(item => Object.keys(item).map(i => item[i]));
console.log(formattedArray)
return (
<DragDropContext onDragEnd={handleDragEnd}>
{_.map(state, (data, key) => {
return(
<div key={key} className={"column"}>
{console.log(key , "KEY")}
<ProjectWrapper className="border">
<h3 className="title">{data.title}</h3>
</ProjectWrapper>
<Droppable droppableId={key}>
{(provided, snapshot) => {
return(
<div>
<div
ref={provided.innerRef}
{...provided.droppableProps}
className={"droppable-col"}
>
<hr className="line" style={{opacity: 10 }}></hr>
{stage.map(stages=>{
if(stages.status == key)
return(
<Draggable key={stages._id}
//index={index}
draggableId={stages._id} className="drag">
{(provided, snapshot) => {
console.log(snapshot)
return(
<div
className={`item ${snapshot.isDragging && "dragging"}`}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
{/* card name */}
<button className="stageDetails" style={{padding: "0",border: "none", background: "none"}}>
{stages.stageName}
</button>
<DeleteOutlinedIcon className="delete" />
</div>
)
}}
</Draggable>
)
})}
{provided.placeholder}
</div>
</div>
)
}}
</Droppable>
</div>
)
})}
</DragDropContext>
</div>
);
};
export default ProjectsDashboard;
I am trying to develop a generic filter component which can have many fields to filter on like color,
size, price range etc and each field might have different types of elements like color may have
checkboxes, radio button and price range might have input element, dropdown etc. To support such varied
cases, I tried to go with this pattern but here I have to iterate the same things multiple times.
I am not sure of this data structure. If anyone has suggestion please help me to improve this code but
the main problem here is "multiple iteration". How can i improve this code?
const filterParams = {
field: {
id : 1, label : 'Field', content: <FieldFilter />
},
employee: {
id : 1, label : 'Employee', content: <Employee />
}
}
<Filter filterParams={filterParams} activeFilterParam="field" />
const Filter = ({ filterParams, activeFilterParam }) => {
const [show, setShow]=useState(false)
return (
<>
<Button secondary icon={filter} onClick={() => setShow(!show)}>Filter</Button>
{show && (
<Card style={{ marginTop: 10 }}>
<Card.Content>
<Tabs activeTab={activeFilterParam}>
<Tabs.List
render={() => {
return (
Object.keys(filterParams).map(filterParam => {
return (
<Tabs.Tab key={filterParam} id={filterParam}>{filterParams[filterParam].label}</Tabs.Tab>
)
}))
}} />
<Tabs.Panels>
{Object.keys(filterParams).map(filterParam => {
return (
<Tabs.Panel key={filterParam} panelId={filterParam}>{filterParams[filterParam].content}</Tabs.Panel>
)
})}
</Tabs.Panels>
</Tabs>
</Card.Content>
<Card.Footer>
<Button>
<Button.Content style={{ marginRight: 10 }}>Save</Button.Content>
<Button.Content secondary onClick={()=>setShow(!show)}>Cancel</Button.Content>
</Button>
</Card.Footer>
</Card>
)}
</>
)
}
If you're not liking the multiple calls to Object.keys(filterParams).map, you could move the loop to the top of the component function. Something like the below might work:
const Filter = ({ filterParams, activeFilterParam }) => {
const [show, setShow]=useState(false)
const {tabs, panels} = Object.keys(filterParams)
.reduce((acc, filterParam) => {
acc.tabs.push(
<Tabs.Tab key={filterParam} id={filterParam}>{filterParams[filterParam].label}</Tabs.Tab>
);
acc.panels.push(
<Tabs.Panel key={filterParam} panelId={filterParam}>{filterParams[filterParam].content}</Tabs.Panel>
);
return acc;
}, { tabs: [], panels: [] });
return (
...
<Card style={{ marginTop: 10 }}>
<Card.Content>
<Tabs activeTab={activeFilterParam}>
<Tabs.List render={() => tabs} />
<Tabs.Panels>
{panels}
</Tabs.Panels>
</Tabs>
...
</Card>
...
)
}
Note that I haven't run this - it likely won't be quite right, but should give the general idea...