question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Dialog] Bad repositioning when closing keyboard after editing a TextField on mobile

See original GitHub issue

Problem Description

Hello!

So, I found the following issue: on mobile phone, when I fill a TextField inside a Dialog, I have a weird behavior appearing:

  • The Dialog opens up with a TextField, everything looks good;
  • When I click on the TextField, the keyboard opens up normally and the Dialog is resized as intended.
  • Then, when I close the keyboard, the Dialog is not re positioned correctly, it is positioned as follows:

screenshot_2016-04-20-11-35-05

The code for the Dialog is the following:

import React from 'react'
import Component from 'react/lib/ReactComponent'
import PureRenderMixin from 'react-addons-pure-render-mixin'
import Moment from 'moment'
import { Map, fromJS } from 'immutable'
import Dialog from 'material-ui/Dialog'
import FlatButton from 'material-ui/FlatButton'
import RaisedButton from 'material-ui/RaisedButton'
import FloatingActionButton from 'material-ui/FloatingActionButton'
import ContentAdd from 'material-ui/svg-icons/content/add'
import TextField from 'material-ui/TextField'
import SelectField from 'material-ui/SelectField'
import MenuItem from 'material-ui/MenuItem'
import DatePicker from 'material-ui/DatePicker/DatePicker'
import TimePicker from 'material-ui/TimePicker/TimePicker'
import AutoComplete from 'material-ui/AutoComplete'
import Divider from 'material-ui/Divider'
import * as Colors from 'material-ui/styles/colors'

import { categories, minDate, maxDate } from './Event'

const createStyle = {
    position: 'fixed'
  ,right: 16
  ,bottom: 16
  ,zIndex: 100
}

const dialogStyle = {
  width: '90%'
  ,minWidth: '320px'
  ,maxWidth: '1080px'
}

export default class CreateEvent extends Component {
  constructor(props) {
    super(props)
    this.shouldComponentUpdate = PureRenderMixin
        .shouldComponentUpdate.bind(this)
    this.state = {
      open: false
            ,owner: this.props.owner
            ,name: this.props.name
            ,startDay: this.props.startTime
            ,startTime: this.props.startTime
            ,endDay: this.props.endTime
            ,endTime: this.props.endTime
            ,description: this.props.description
            ,location: this.props.location
            ,category: this.props.category
            ,nameError: null
            ,startDayError: null
            ,startTimeError: null
            ,endDayError: null
            ,endTimeError: null
            ,descriptionError: null
            ,locationError: null
            ,categoryError: null

  }}

  componentWillReceiveProps(newProps) {
      if (newProps.owner !== this.state.owner) {
        this.setState({ owner: newProps.owner });
      }
    }

    handleOpen() {
    this.setState({ open: true })
  }

  handleOk() {
    const fields = fromJS({
        name: 'name' 
        ,startDay: 'starting day'
        ,startTime: 'starting time'
        ,endDay: 'ending day'
        ,endTime: 'ending time'
        ,description: 'description'
        ,location: 'location'
        ,category: 'category'
    })
    const valid = fields.keySeq().reduce((valid, field) => {
        if (!this.state[field] || this.state[field] === '') {
            this.setState({
                [field + 'Error']: 'The event ' + fields.get(field) + 
                    ' cannot be empty'
            })
            return false
        }
        return valid
    }, true)
    if (valid) {
        const event = fromJS({
            name: this.state.name
            ,startTime: Moment(
                Moment(this.state.startDay).format('YYYY-MM-DD ') +
                Moment(this.state.startTime).format('HH:mm:ss')
                ,'YYYY-MM-DD HH:mm:ss'
            )
            ,endTime: Moment(
                Moment(this.state.endDay).format('YYYY-MM-DD ') +
                Moment(this.state.endTime).format('HH:mm:ss')
                ,'YYYY-MM-DD HH:mm:ss'
            )
            ,owner: this.state.owner
            ,description: this.state.description
            ,location: this.state.location
            ,category: this.state.category.get('name')
        })
        if (event.get('endTime') > event.get('startTime')) {
            (this.props.create) ?
                this.props.postEvent(event) :
                this.props.updateEvent(event, this.props.eventId)
            this.setState({open: false})
        } else {
            this.setState({
                endDayError: 'The ending day should be after the starting day'
                ,endTimeError: 'The ending time should be after the starting time'
            })
        }
    }
  }

  handleCancel() {
    this.setState({open: false})
  }

  handleNameChange(event) {
    this.setState({
        nameError: null
        ,name: event.target.value
    })
  }

  handleLocationChange(text) {
    this.setState({
        locationError: null
        ,location: text
    })
  }

  handleCategoryChange(event, index, value) {
    this.setState({
        categoryError: null
        ,category: value
    })
  }

  handleStartDayChange(event, time) {
    this.setState({
        startDayError: null
        ,startDay: time
    })
  }

  handleStartTimeChange(event, time) {
    this.setState({
        startTimeError: null
        ,startTime: time
    })
  }

  handleEndDayChange(event, time) {
    this.setState({
        endDayError: null
        ,endDay: time
    })
  }

  handleEndTimeChange(event, time) {
    this.setState({
        endTimeError: null
        ,endTime: time
    })
  }

  handleDescriptionChange(event) {
    this.setState({
        descriptionError: null
        ,description: event.target.value
    })
  }

  render() {
    const actions = [
      <FlatButton
        label="Ok"
        primary={true}
        onTouchTap={::this.handleOk}
      />
      ,<FlatButton
        label="Cancel"
        secondary={true}
        onTouchTap={::this.handleCancel}
      />
    ]

    return (
      <div>
                {(this.props.create) ? (
                    <FloatingActionButton 
                        style={createStyle} 
                        backgroundColor={Colors.deepOrange700}
                        onTouchTap={::this.handleOpen}
                    >
                <ContentAdd />
                </FloatingActionButton>
            ) : (
                <RaisedButton 
                    label="Edit" 
                    fullWidth={true}
                    primary={true}
                        onTouchTap={::this.handleOpen}
                />
            )}
        <Dialog
          title={
            (
                (this.props.create) ? 
                "Create a new" : 
                "Edit " + ((this.props.isOwner) ?  "your" : "this")
            ) + 
            " awesome event!"
          }
          titleStyle={(this.state.category) ?
            {backgroundColor: this.state.category.get('bgColor') || 'white'} :
            null}
          actions={actions}
          modal={false}
          open={this.state.open}
          onRequestClose={::this.handleCancel}
          contentStyle={dialogStyle}
          autoScrollBodyContent={true}
        >
            <TextField
                hintText='Event title'
                value={this.state.name}
                errorText={this.state.nameError}
                fullWidth={true}
                onChange={::this.handleNameChange}
                disabled={!this.props.isOwner && !this.props.create}
            />
            <div className='container-fluid'>
                <div className='col-sm-6 col-xs-12'>
                    <AutoComplete 
                        hintText="Location"
                        errorText={this.state.locationError}
                        dataSource={[]}
                        onUpdateInput={::this.handleLocationChange}
                        searchText={this.state.location}
                        fullWidth={true}
                    />
                </div>
                <div className='col-sm-6 col-xs-12'>
                    <SelectField
                        floatingLabelText="Category"
                        errorText={this.state.categoryError}
                        onChange={::this.handleCategoryChange}
                        value={this.state.category}
                        disabled={!this.props.isOwner && !this.props.create}
                        fullWidth={true}
                        labelStyle={(this.state.category) ?
                                    {color: this.state.category.get('color') || 'white'} :
                                    null}
                    >
                        {categories.map((category, index) => (
                            <MenuItem
                                key={index}
                                style={{color: category.get('color')}}
                                value={category}
                                primaryText={category.get('name')}
                            />
                        ))}
                    </SelectField>
                </div>
            </div>
            <div className='col-sm-7 col-xs-12'>
                <DatePicker 
                  minDate={minDate}
                  maxDate={maxDate}
                  defaultDate={minDate}
                  disableYearSelection={true}
                    hintText="Start day"
                    errorText={this.state.startDayError} 
                    fullWidth={true} 
                    onChange={::this.handleStartDayChange}
                    value={this.state.startDay}
                />
            </div>
            <div className='col-sm-5 col-xs-offset-2 col-xs-10'>
                <TimePicker 
                    format='24hr'
                    hintText="Start time"
                    errorText={this.state.startTimeError}
                    fullWidth={true}
                    onChange={::this.handleStartTimeChange}
                    value={this.state.startTime}
                />
            </div>
            <div className='col-sm-7 col-xs-12'>
                <DatePicker
                  minDate={minDate}
                  maxDate={maxDate}
                  defaultDate={maxDate}
                  disableYearSelection={true}
                  hintText="End day"
                    errorText={this.state.endDayError}
                  fullWidth={true}
                    onChange={::this.handleEndDayChange}
                    value={this.state.endDay}
                />
            </div>
            <div className='col-sm-5 col-xs-offset-2 col-xs-10'>
                <TimePicker 
                    format='24hr'
                    hintText="End time"
                    errorText={this.state.endTimeError}
                    fullWidth={true}
                    onChange={::this.handleEndTimeChange}
                    value={this.state.endTime}
                />
            </div>
            <TextField
                hintText='Description'
                errorText={this.state.descriptionError}
                fullWidth={true}
                multiLine={true}
                        onChange={::this.handleDescriptionChange}
                    value={this.state.description}
                    disabled={!this.props.isOwner && !this.props.create}
            />
        </Dialog>
      </div>
    )
  }
}

Versions

  • Material-UI: 0.15.0-beta.1
  • React: 15.0.1
  • Browser: Chrome on Android (OnePlus Two)

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:8 (3 by maintainers)

github_iconTop GitHub Comments

3reactions
w01fgangcommented, May 13, 2016

Try to add repositionOnUpdate={false} to Dialog component.

<Dialog 
   repositionOnUpdate={false} 
   open={this.props.open}
   autoScrollBodyContent={true}
>
...
</Dialog>
2reactions
darkowiccommented, Nov 12, 2016

We’ve had huge problems with this and generally responsiveness of Dialog. Especially when opening keyboard on mobile devices. We are writing application that is responsive and should works on every device. Second problem is that the gap between top screen and the top of dialog. It stays same for small devices. screenshot_2016-11-12_20-14-23 For example in angular implementation it is smaller when the window height is smaller. screenshot_2016-11-12_20-17-30

As a workaround we did some css tricks and we maximize it for small devices and it works well for mobile devices. screenshot_2016-11-12_20-23-14

I expected similar behavior from mui’s Dialog. It should occupy almost 100% of visible place on small devices to works well.

Read more comments on GitHub >

github_iconTop Results From Across the Web

[Dialog] Bad repositioning when closing keyboard after ...
Hello! So, I found the following issue: on mobile phone, when I fill a TextField inside a Dialog, I have a weird behavior...
Read more >
Android - prevent dialog dimiss closing soft keyboard
I am attempting to force the soft keyboard to display when an EditText gains focus, despite whether or not the user clicked on...
Read more >
jQMobile re-positions popup every time when Android ...
I've developed a PhoneGap app using Jquery Mobile 1.4.3 . My login page is a popup. And I faced with an endless process...
Read more >
PySimpleGUI
A line of text, a line of text and an input area, and finally ok and cancel buttons. This makes the coding process...
Read more >
Toolbar Panels & Icons
Editing the Tool Bar - The panels and Icons can be reordered in any way, undocked and docked to the top or bottom...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found