I'm writing dialog component in React JS. I want to make it larger. I thought, that it could be possible with using of grid. unfortunately it isn't so. How I remember in bootstrap I passed lg-class, how this issue can be solved in office365 dialog?
My component:
export default class CalendarDialog extends Component {
static defaultProps = {
allowTextInput: false,
formatDate: (date) => {
if (date) {
return date.toDateString();
}
return '';
},
firstDayOfWeek: DayOfWeek.Sunday,
isRequired: false,
isMonthPickerVisible: true,
strings: DEFAULT_STRINGS,
borderless: false,
pickerAriaLabel: 'Calender',
};
constructor(props) {
super();
let { formatDate, value } = props;
this.state = {
selectedDate: value || new Date(),
};
}
_onSelectDate = (date) => {
console.log('state', this.state);
this.setState({
selectedDate: date,
});
}
_calendarDismissed = () => {
this._dismissDatePickerPopup();
}
render(){
let {
show,
onClose
} = this.props;
const {
firstDayOfWeek,
strings,
} = this.props;
return (
<div className="ms-Grid">
<div className="ms-Grid-row">
<div className="ms-Grid-col ms-sm6">
<Dialog
isOpen={ show }
type={ DialogType.normal }
onDismiss={ onClose }
title='Version'
subText=''
isBlocking={ false }
containerClassName='ms-dialogMainOverride'
>
<VersionList/>
<DialogFooter>
<DefaultButton onClick={ onClose } text='Hey' />
<PrimaryButton onClick={ onClose } text='Save' />
<DefaultButton onClick={ onClose } text='Close' />
</DialogFooter>
</Dialog>
</div>
</div>
</div>
);
}
}
It should be enough to add a CSS rule containing
.ms-dialogMainOverride {
max-width: 600px;
}
But keep in mind not to put too much information and/or choices into the dialog and that the containerClassName property is marked in documentation as deprecated.
Related
I am using React and rendering a modal from Material UI. The way the modal is rendered is it has to be a part of the body of code. So I added it to the bottom of the page. The state determines whether or not the modal is open. The issue is that I can see that a function that is in the modal is being called multiple times. And very rapidly. Like more than once each second. Please see my code:
class ComponentName extends React.Component {
constructor(props) {
super(props);
this.state = {
countries: [],
isButtonDisabled: false,
formError: false,
showModalCta: false,
showModal: false,
iframeUrl: ''
};
}
handleClose = () => {
this.setState({
showModal: false
});
};
handleShowModal = () => {
this.setState({
showModal: true
})
}
showModalContent = () => {
const { classes } = this.props;
const { iframeUrl } = this.state;
getiframeUrl().then((res) => {
this.setState({
iframeUrl: res.level2VerificationUrl
});
});
return (
<Paper className={classes.modalPaper}>
<iframe src={iframeUrl} width="500px" height="500px" />
</Paper>
);
};
render() {
const {
classes, history, firstName, lastName, country, region, address, city, dob, phone, smsCode, postalCode, actions
} = this.props;
const {
countries, formError, isButtonDisabled, showCta, showModal
} = this.state;
return (
<div>
<Modal className={classes.modal} open={showModal} onClose={this.handleClose}>
{this.showModalContent()}
</Modal>
</div>
);
}
}
Its pretty much calling that function to get the url every second. But I dont quite understand why this is the behavior. Been doing research on this but no answers. Is there any way to prevent this? Thanks!
showModalContent will be executed on every "state change" of the component (on every render).
There (from what I see) you are making a call to a promise (getiframeUrl) and you are setting the state of the component (which makes it change state).
Hence: Render -> showModalContent -> change state -> re-render -> showModalContent -> ... (infinite loop).
My advice is that you do the setState of iframeUrl only in the componentDidMount. Something like this:
componentDidMount() {
const { iframeUrl } = this.state;
getiframeUrl().then((res) => {
this.setState({
iframeUrl: res.level2VerificationUrl
});
});
}
showModalContent = () => {
const { classes } = this.props;
return (
<Paper className={classes.modalPaper}>
<iframe src={iframeUrl} width="500px" height="500px" />
</Paper>
);
};
I have a search bar on a children component who's displaying a pannel when i'm writing inside,
and I'm trying to close it on my parent when I'm clicking outside of this pannel.
So I need a solution to set a state to false at this moment.
Any idea ?
Here's an example of my code:
[parent.js]
constructor(props) {
super(props)
this.state = {
showResults: false
}
}
render() {
return (
<div onClick={() => this.setState({showResults: false})}>
<div>
<Children showResults={this.state.showResults}/>
</div>
</div>)
}
[children.js]
constructor(props) {
super(props)
this.state = {
showResults: this.props.showResults
}
}
getResults(event) {
this.setState({input: event.target.value, showResults: true})
}
render() {
return (<div>
<Input onChange={this.getResults.bind(this)}/>
this.state.showResults ?
<div>PANNEL</div>
: ''
</div>)
}
When the panel is open, create a transparent div that covers the screen behind your panel with an onClick property to change the state.
I finally did it using Redux, I have created a ShowPannel reducer who just have a boolean visible state inside. Here's my code:
[My view] :
render() {
return (<div onClick={() => this.props.showPannel(false)}>
{
this.props.visible
? <div className="listResults"></div>
: ''
}
</div>)
}
function mapStateToProps(state) {
return {visible: state.showPannelReducer.visible};
}
function mapDispatchToProps(dispatch) {
return {
showPannel: (visible) => dispatch(showPannel(visible))
}
}
Home = connect(mapStateToProps, mapDispatchToProps)(Home);
export default Home;
[My reducer] :
import {SHOW_PANNEL} from '../actions/pannel/types';
const initialState = {
visible: false
};
function showPannelReducer(state = initialState, action) {
switch (action.type) {
case SHOW_PANNEL:
return {
...state,
visible: action.visible
}
default:
return state
}
};
export default showPannelReducer;
I would like to create component API for dialog window, based on https://github.com/reactjs/react-modal
I want render react component with renderDOM, get component instance and call API of modal, it does mean for example open(), close() etc.
So, more precisely I would like to work with current state of component, (like API works) not with props.
I have simple base class for all modals:
export class BaseModal extends Modal {
constructor() {
super();
this.state = BaseModal._getInitState();
}
static get style() {
return {
content: {
top: '50%',
left: '50%',
right: 'auto',
bottom: 'auto',
marginRight: '-50%',
transform: 'translate(-50%, -50%)'
}
};
}
open() {
this.setState({isOpen: true});
}
close() {
this.setState({isOpen: false});
}
render() {
return this.state.isOpen ? (
<div className="modal modal__{this.name}">
<Modal isOpen={this.state.isOpen}
onRequestClose={this.close}
style={BaseModal.style}
contentLabel={this.getHeaderContent()}
parentSelector={BaseModal._getParentSelector}>
<button onClick={this.close}>X</button>
<div className="modal__body">
{this.getBodyContent()}
</div>
<div className="modal__footer">
{this.getFooterContent()}
</div>
</Modal>
</div>
) : null;
}
getHeaderContent() {
throw new Error("Not implement in child class.");
}
getBodyContent() {
throw new Error("Not implement in child class.");
}
getFooterContent() {
throw new Error("Not implement in child class.");
}
static _getInitState() {
let state = {};
state.isOpen = false;
}
}
Now I have child component:
export class RecommendTripModal extends BaseModal {
getHeaderContent() {
return "Test modal dialog";
}
getBodyContent() {
return <p>Test modal body</p>;
}
getFooterContent() {
return <p>Test modal footer</p>;
}
}
Ok, this is fine, but now I want to call something like this:
let recommendedTripModal = ReactDOM.render(React.createElement(RecommendTripModal, null), document.querySelector("#modals"));
//open dialog
recommendedTripModal.open();
But now is problem with context. Because this.state.isOpen has RecommendTripModal context and state is null. Is there way, how to solved this problem with react? And is this solid way? Or I should create required API different way?
Thank you for your time!
Okay, let's dig a little bit deeper here, best way is to use React context and HoC magic power
Modal.js
import React from "react";
import PropTypes from "prop-types";
import { omit } from "lodash";
export class Modal extends React.Component {
static contextTypes = {
modalOpen: PropTypes.bool
};
static propTypes = {
children: PropTypes.node
};
render() {
if (!this.context.modalOpen) return null;
return (
<div>
<h1>I am base modal title</h1>
<div>{this.props.children}</div>
</div>
);
}
}
export class ModalContext extends React.Component {
static childContextTypes = {
modalOpen: PropTypes.bool
};
static defaultProps = {
onOpen: () => {},
onClose: () => {}
};
static propTypes = {
children: PropTypes.func.isRequired
};
constructor(...args) {
super(...args);
this.handleOpen = this.handleOpen.bind(this);
this.handleClose = this.handleClose.bind(this);
}
state = {
isOpen: false
};
getChildContext() {
return {
modalOpen: this.state.isOpen
};
}
handleClose() {
if (this.state.isOpen) {
this.setState({ isOpen: false });
}
}
handleOpen() {
if (!this.state.isOpen) {
this.setState({ isOpen: true });
}
}
render() {
const { identity, children } = this.props;
return children({
[identity]: {
open: this.handleOpen,
close: this.handleClose,
isOpen: this.state.isOpen
}
});
}
}
export default function modal(initialModalProps = {}) {
return function(Component) {
const componentName =
Component.displayName || Component.name || "Component";
return class extends React.Component {
static displayName = `Modal(${componentName})`;
static propTypes = {
identity: PropTypes.string
};
static defaultProps = {
identity: "modal"
};
render() {
const { identity } = this.props;
return (
<ModalContext
identity={identity}
{...initialModalProps}
{...this.props[identity]}
>
{modalProps => (
<Component
{...omit(this.props, identity, "identity")}
{...modalProps}
/>
)}
</ModalContext>
);
}
};
};
}
HelloWorldModal.js
import React from "react";
import withModal, { Modal } from "./modal";
class HelloWorldModal extends React.Component {
render() {
const { modal } = this.props;
return (
<div>
<button type="button" onClick={modal.open}>
Open Modal
</button>
<button type="button" onClick={modal.close}>
Close Modal
</button>
<Modal>Yeah! I am sample modal!</Modal>
</div>
);
}
}
export default withModal()(HelloWorldModal);
In case you are lazy, I prepared a codesandbox with working code :)
https://codesandbox.io/s/2oxx2j4270?module=%2Fsrc%2FHelloWorldModal.js
I have an entity with a date that within a datepicker component I need to change for a new date. To do that I'm ussing a popup. The popup work flawlessly if I click the datepicker and handle the change with the function this.props.handleDateChange but if the users types in the date with the keyboard I can't catch the new date and modify my parent state with the value in my datepicker this.props.myEntityCurrentDate. How can I achieve this?
I have the following HOC that corresponds to an popup in react. It receives a body and a footer.
const createPopup = (Body, Footer) => {
class Popup extends React.PureComponent {
render() {
return <div className='popup-react'>
<div className='popup-react-content'>
<div>
<div>
<span>{this.props.title}</span>
</div>
<div>
<Body {...this.props} />
</div>
<div>
<Footer {...this.props} />
</div>
</div>
</div>
</div>;
}
}
return Popup;
}
And I create the following body and footer for the desired page
class PopupBody extends React.PureComponent {
render() {
return <div>
<DatePicker
dateFormat="MM/DD/YYYY"
value={this.props.myEntityCurrentDate}
onChange={this.props.handleDateChange}
/>
</div>;
}
}
class PopupFooter extends React.PureComponent {
render() {
return <div>
<button type="button">
<span onClick={() => this.props.handleSubmit(this.props.myEntityId)}>Accept</span>
</button>
<button type="button" onClick={this.props.closePopup}>
<span className='ui-button-text'>Cancel</span>
</button>
</div>;
}
}
const Popup = createPopup(PopupBody, PopupFooter);
The component that will make use of those components and render the popup is the following:
class MyEntity extends React.Component {
constructor(props) {
super(props);
this.state = {
showPopup: true,
newDate: null,
errors: []
};
//bind of this
};
}
handlePopupDateChange(dueDate) {
this.setState({
newDate: dueDate,
errors: []
});
}
handlePopupSubmit(id) {
var validations = {
newDate: {
presence: {
allowEmpty: false, message: " can't be empty"
}
}
};
validate(this.state, validations, () => this.submitPopupForm(id), this.renderPopupErrors);
}
submitPopupForm(id) {
var vm = { Id: parseInt(id), DueDate: this.state.newDate ? this.state.newDate.format("DD-MM-YYYY") : null }
this.props.apiClient.post('/api/MyEntity/Put/',
vm,
this.formPopupSucceeded,
this.formPopupFailed);
}
formPopupSucceeded() {
this.togglePopup();
window.location.reload();
}
formPopupFailed(response) {
this.togglePopup();
this.renderPopupErrors([response.Message]);
}
renderPopupErrors(validationErrors) {
this.togglePopup();
this.setState({ errors: validationErrors });
}
togglePopup() {
this.setState({
showPopup: !this.state.showPopup,
newDate: null,
});
}
render() {
return <div>
<If condition={this.state.showPopup}>
<Popup
title='My Popup Title'
closePopup={this.togglePopup}
myEntityCurrentDate={myMappedEntity.Date}
handleDateChange={this.handlePopupDateChange}
handleSubmit={this.handlePopupSubmit}
Date={this.state.newDate}
myEntityId={myMappedEntity.Id} />
</If>
</div>;
}
}
Currently on my react app, I am loading many div's which is being dynamically loaded with info from a database. I am trying to make it so when I click on one of these div's a Pop-up emerges, with more in depth data related to that div. However, it does not seem to work as expected. The onClick does not work with this dynamically loaded div. I tried testing the pop-up on a standard button element on my main App component and it worked. Here is my code:
class ResultBox extends Component {
constructor(props){
super(props);
this.state = {
showPopup: false,
posts: []
};
}
togglePopup() {
this.setState({
showPopup: !this.state.showPopup
});
}
componentDidMount() {
axios.get('http://localhost:3001/api/events')
.then(res => {
let posts = res.data.map(obj => obj);
this.setState({posts});
console.log(posts);
});
}
render() { ********** Below here is where my issue is *****
return (
this.state.posts.map(events =>
<div key={events.key}
className='result_box'
onClick={this.togglePopup.bind(this)}>
<p>{events.name}</p>
{events.place && events.place.location && <p>
{events.place.location.city}</p>}
</div>
)
{this.state.showPopup ?
<Result
text='Close Me'
closePopup={this.togglePopup.bind(this)}
/>
: null
}
);
}
}
And this ResultBox is being rendered in App
class App extends Component {
render() {
return (
<div className="App">
<NavBar className="navbar-body"/>
<div className="spacer"></div>
<p className="App-intro">
Find a jam
</p>
<ResultBox />
</div>
);
}
}
The Result is simply the pop-up box component. If anyone knows how I can get this to work it would be much appreciated.
Yes, you need to have your posts data in a div, this is how I would structure it.
class ResultBox extends Component {
constructor(props){
super(props);
this.state = {
showPopup: false,
posts: []
};
this.togglePopup = this.togglePopup.bind(this);
}
togglePopup() {
this.setState({
showPopup: !this.state.showPopup
});
}
componentDidMount() {
axios.get('http://localhost:3001/api/events')
.then(res => {
let posts = res.data.map(obj => obj);
this.setState({posts});
console.log(posts);
});
}
buildRows() {
return this.state.posts.map( (events, index) =>
<div key={index} className='result_box' onClick={this.togglePopup}>
<p>{events.name}</p>
{events.place && events.place.location &&
<p>{events.place.location.city}</p>
}
</div>
);
}
render() {
let rows = this.buildRows();
return (
<div>
{rows}
{this.state.showPopup &&
<Result text='Close Me'closePopup={this.togglePopup.bind(this)}/>
}
</div>
);
}
}
export default ResultBox;