Following the Ionic documentation, I am trying to get the popover sticky to the button (like on their own example).
Unfortunately I do not know how to achieve this...
Thanks
import React, { useState } from 'react';
import { IonPopover, IonButton } from '#ionic/react';
export const PopoverExample: React.FC = () => {
const [showPopover, setShowPopover] = useState(false);
return (
<>
<IonPopover
isOpen={showPopover}
onDidDismiss={e => setShowPopover(false)}
>
<p>This is popover content</p>
</IonPopover>
<IonButton onClick={() => setShowPopover(true)}>Show Popover</IonButton>
</>
);
};
You also need to include an event in the showPopover hook -
const [showPopover, setShowPopover] = useState<{open: boolean, event: Event | undefined}>({
open: false,
event: undefined,
});
<IonPopover
isOpen={showPopover.open}
event={showPopover.event}
onDidDismiss={e => setShowPopover({open: false, event: undefined})}
>
<p>This is popover content</p>
</IonPopover>
<IonButton onClick={(e) => setShowPopover({open: true, event: e.nativeEvent})}>Click</IonButton>
Related
i have a modal component in my react app and i need to close it on click outside
import React from "react";
import ReactDOM from "react-dom";
import style from "./Modal.module.scss";
const Modal = ({ isShowing, hide, childrenContent, childrenHeader }) =>
isShowing
? ReactDOM.createPortal(
<React.Fragment>
<div className={style.modalOverlay} />
<div
className={style.modalWrapper}
aria-modal
aria-hidden
tabIndex={-1}
role="dialog"
>
<div className={style.modal}>
<div className={style.modalHeader}>
{childrenHeader}
<button
type="button"
className={style.modalCloseButton}
data-dismiss="modal"
aria-label="Close"
onClick={hide}
>
<span aria-hidden="true">×</span>
</button>
</div>
{childrenContent}
</div>
</div>
</React.Fragment>,
document.body
)
: null;
export default Modal;
i was try to use this solution but it's not work in my code, how can i fix it?
Just a tip, when looking at the html you can use the native <dialog> tag, this is the semantically correct way to display a dialog type pop-up box, which yours looks to be.
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog
Dialog has a showModal() method, and a .close() method. This would be a better way of displaying a pop-up type dialog, than using <div> tags. It also allows you to use the native HTML5 methods, rather than trying to provide a work around using React.
I would reccomend this method over trying to look for work arounds
const Modal = ({ children, showModal, toggleModal }) => {
const wrapperRef = React.useRef(null);
const closeModal = React.useCallback(
({ target }) => {
if (
wrapperRef &&
wrapperRef.current &&
!wrapperRef.current.contains(target)
) {
toggleModal();
}
},
[toggleModal]
);
React.useEffect(() => {
document.addEventListener("click", closeModal, { capture: true });
return () => {
document.removeEventListener("click", closeModal, { capture: true });
};
}, [closeModal]);
return showModal
? ReactDOM.createPortal(
<>
<div ref={wrapperRef} className="modal">
{children}
</div>
</>,
document.body
)
: null;
};
Modal.propTypes = {
children: PropTypes.node.isRequired,
showModal: PropTypes.bool.isRequired,
toggleModal: PropTypes.func.isRequired
};
export default Modal;
in your parent component :
const Parent = () => {
const [showModal, setModalState] = React.useState(false);
const toggleModal = React.useCallback(() => {
setModalState((prevState) => !prevState);
}, []);
return (
<div>
<Modal showModal={showModal} toggleModal={toggleModal}>
<h1>Hello!</h1>
... some other childrens
<button
onClick={toggleModal}
>
Close
</button>
</Modal>
</div>
);
};
I am trying to create a modal and for some reason I cannot interact with any components like buttons or inputs within my modal.
I am building a React Portal, so that my modal and page are separate from each other and that the modal is not "running" in the background when it is not in use
import {useState, useLayoutEffect} from "react"
import { createPortal } from "react-dom"
const createWrapperAndAppendToBody = (wrapperId: string) => {
if(!document) return null
const wrapperElement = document.createElement("div")
wrapperElement.setAttribute('id', wrapperId)
document.body.appendChild(wrapperElement)
return wrapperElement
}
function ReactPortal({children, wrapperId}: {children: React.ReactElement; wrapperId: string}) {
const [wrapperElement, setWrapperElement] = useState<HTMLElement>()
useLayoutEffect(() => {
let element = document.getElementById(wrapperId)
let systemCreated = false
if(!element) {
systemCreated = true
element = createWrapperAndAppendToBody(wrapperId)
}
setWrapperElement(element!)
return () => {
if(systemCreated && element?.parentNode) {
element.parentNode.removeChild(element)
}
}
},[wrapperId])
if(!wrapperElement) return null
return createPortal(children, wrapperElement)
}
export default ReactPortal
I am pretty sure this is fine.
import React, {useEffect} from "react";
import ReactPortal from "./ReactPortal";
interface ConfirmationModalProps {
isOpen: boolean;
handleClose: () => void
children: React.ReactNode
}
export const ConfirmationModal =({children, isOpen, handleClose}:ConfirmationModalProps) => {
//allows to press Escape Key
useEffect(() => {
const closeOnEscapeKey = (e: KeyboardEvent) =>
e.key === 'Escape' ? handleClose() : null
document.body.addEventListener('keydown', closeOnEscapeKey)
return () => {
document.body.removeEventListener('keydown', closeOnEscapeKey)
};
},[handleClose])
//stops scrolling fuction when modal is open
useEffect(() => {
document.body.style.overflow = 'hidden';
return (): void => {
document.body.style.overflow = 'unset'
}
},[isOpen])
if(!isOpen) return null
return (
<ReactPortal wrapperId='react-portal-modal-container'>
<>
<div className='fixed top-0 left-0 w-screen h-screen z-40 bg-neutral-800 opacity-50' />
<div className='fixed rounded flex flex-col box-border min-w-fit overflow-hidden p-5 bg-zinc-800 inset-y-32 inset-x-32'>
<button className='py-2 px-8 self-end font-bold hover:bg-violet-600 border rounded'
onClick={() => console.log('Pressed')}>
Close
</button>
<div className='box-border h-5/6'>{children}</div>
</div>
</>
</ReactPortal>
)
}
This is my reusable Modal component so that I do not have to build a new modal from scratch. The close button within here is no also not working/can't event click on it via a console.log('pressed')
Here is a picture of the modal, but none of the buttons work, however, my Escape key function does work. Any thoughts would be great!
I am trying to add side toggle menu box in main page which is positioning on right side of document when the button is clicked.
I made a function for toggling action(you can check the function below) with some of react hooks and added by using onClick() method. I clicked the btn to check if it works, but it doesn't. I changed onClick() method to onMouseEnter() and it worked. I added callback(onClick(()=>{function()})) but it still doesn't work.
I think toggling function doesn't have any problem(because it worked properly when it's on onMouseEnter). Some mechanisms of them makes the difference. I checked the docs of javascript but it was not helpful.
I wish somebody provide me a demonstration of this.
Here is my codes.
import React, { useState } from "react";
import "../css/side-toggle.css";
import hbgBtn from "../image/hamburgerBtn.png";
const SideBar = ({ wid = 380, children }) => {
const [isOpen, setIsOpen] = useState(false);
const toggleMenu = () => {
setIsOpen((isOpen) => !isOpen);
console.log('1')
};
const closeMenu = () => {
setIsOpen((isOpen) => !isOpen);
}
return (
<div className="container" wid={wid}>
<img onMouseEnter={(e) => toggleMenu(e)}
className={!isOpen ? "show-btn" : "hide"}
src={hbgBtn}
alt=""
/>
<div onMouseEnter={closeMenu} className={isOpen? "dimmer" : 'hide'}>{children}</div>
<div className={isOpen ? "side-column" : "hide"}></div>
</div>
);
};
export default SideBar;
(and i will appreciate when you understand my wierd English. I'm ESL)
There is a possiblity that you may have made some error while styling. I have tried my best to replicate your code and it works for me fine.
import React, { useState } from 'react';
import './style.css';
const SideBar = ({ wid = 380, children }) => {
const [isOpen, setIsOpen] = useState(false);
const toggleMenu = () => {
setIsOpen((isOpen) => !isOpen);
console.log('1');
};
const closeMenu = () => {
setIsOpen((isOpen) => !isOpen);
};
return (
<div className="container">
<div onClick={(e) => toggleMenu(e)}>
<img
className={!isOpen ? 'show-btn' : 'hide'}
src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/b2/Hamburger_icon.svg/1024px-Hamburger_icon.svg.png"
alt=""
/>
</div>
<div onMouseEnter={closeMenu} className={isOpen ? 'dimmer' : 'hide'}>
{children}
</div>
<div className={isOpen ? 'side-column' : 'hide'}></div>
</div>
);
};
export default SideBar;
Here is the stackblitz code link
https://stackblitz.com/edit/react-xtwxzc?file=src/App.js
How to add or remove css classes from an element in react functional component?
setup:
export default function App() {
const menuOptions = ["home", "blog", "feedback"];
const [classes, setClasses] = useState("btn btn-secondary m-1");
const [activeIndex, setActiveIndex] = useState(0);
generate menu buttons:
const renderMenu = menuOptions.map((menuItem, indx) => {
return (
<button
key={indx}
className={classes}
onClick={(key) => handleClick(key)}
>
{menuItem}
</button>
);
});
rendering:
return (
<div className="App">
<h1>Add Remove CSS classes</h1>
{renderMenu}
</div>
);
}
I have just started learning React so I am struggling with adding/removing classes.
sample code: https://codesandbox.io/s/tender-poincare-qw64m0?file=/src/App.js
I currently struggle to finish a story in React story for one of my components : (images below)
My component receives a props from a parent, a boolean and a function to modify this boolean. When I click on a button it should change the value of this boolean (false to true or true to false).
I can't seem to test this behaviour on storybook. I don't know if I do things the right way, but it seems impossible to pass a function from my .Stories filecode to my component to test it.
My question is : Am i doing things the right way and is storybook built for this kind of test ?
story file code :
import React from 'react';
import { ComponentStory, ComponentMeta } from '#storybook/react';
import { ModelCard } from './';
export default {
title: 'ModelCard',
component: ModelCard,
argTypes: {
yearProduct: { control : 'text'},
ecoDesigned: { control: 'boolean'},
titleProduct: {control: 'text'},
photoProduct: {control: 'text'},
setEcoDesigned: {action: 'clicked'}
}
} as ComponentMeta<typeof ModelCard>;
const Template: ComponentStory<typeof ModelCard> = (args) => <ModelCard {...args}/>;
export const ModelCardCompleteControls = Template.bind({});
ModelCardCompleteControls.args = {
yearProduct: '2018',
ecoDesigned: false,
titleProduct: '66180 - W200 S | 1019507 - ATHLLE Watches or Stopwatche 7026 2021 | GEDS',
photoProduct: 'https://picsum.photos/200',
};
My component code :
import React from 'react';
import { useState } from 'react';
import { VtmnButton, VtmnIcon } from '#vtmn/react';
import { EcoDesignedDot } from './EcoDesignedDot';
import './modelcard.scss';
interface ModelCardProps {
photoProduct: string;
yearProduct: string,
titleProduct: string,
ecoDesigned: boolean;
setEcoDesigned: (ecoDesigned: boolean) => void;
}
export const ModelCard = ({ yearProduct, titleProduct, photoProduct, ecoDesigned, setEcoDesigned }: ModelCardProps) => {
const [open, setOpen] = useState(false);
return (
<article className="model-card">
<section className="vtmn-grid vtmn-grid-cols-12 vtmn-items-center vtmn-space-y-5">
<p className="vtmn-col-span-1">{yearProduct}</p>
<img className="vtmn-col-span-1"
style={{ borderRadius: 5 }}
src={photoProduct} width={60}
height={60} />
<p className="vtmn-col-span-6">{titleProduct}</p>
<div className="vtmn-col-span-3">
<EcoDesignedDot ecoDesigned={ecoDesigned}/>
</div>
<div className="vtmn-col-span-1" onClick={() => setOpen(!open)}>
<VtmnIcon value="arrow-up-s-line" className={open ? 'reversed_angle' : 'original_angle'} />
</div>
</section>
<section className="vtmn-grid vtmn-grid-cols-12">
{
open && <div className="vtmn-col-start-3 vtmn-col-span-5">
<p>
Votre produit est-il éco-design ?
</p>
<VtmnButton onClick={() => setEcoDesigned(true)} variant={ecoDesigned ? 'primary' : 'secondary'} size="medium">Oui</VtmnButton> // This is what I'm talking about
<VtmnButton onClick={() => setEcoDesigned(false)} variant={ecoDesigned ? 'secondary' : 'primary'} size="medium">Non</VtmnButton> // This is what I'm talking about
</div>
}
</section>
</article>
);
};
You can add useState in the Template function because it's a react component but, make sure that you send these values to the ModelCard component properly.
const Template: ComponentStory<typeof ModelCard> = (args) => {
const [ecoDesigned, setEcoDesigned] = useState(false);
return <ModelCard {...args} ecoDesigned={ecoDesigned} setEcoDesigned={setEcoDesigned}/>;
};
The args object will have all the default props. So, you should make sure that these are overwritten.