I am trying to implement KeyboardDatepicker inside ag grid's cell editor. When I select a date from the datepicker popup, the month value is shown incorrectly. The date I have selected is 30-04-2020 and the date it is showing is 30-30-2020. I tried using formatDate attribute to format the date as well. I am passing the selected value in proper format but the date is showing incorrectly. I am using date-io/moment version 1.3.13 and date-io/date-fns version 0.0.2. Anybody faced this issue before? I am sure this is a trivial issue and I am missing something. Any pointers would be much appreciated. Thanks in advance. Cheers!
Update:
Datepicker_component_grid.js
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import { KeyboardDatePicker, MuiPickersUtilsProvider } from '#material-ui/pickers';
import DateFnsUtils from '#date-io/date-fns'
import moment from 'moment'
import format from 'date-fns/format'
import Grid from '#material-ui/core/Grid'
import { SampleDatePickerWithUtils } from '../Sample_datepicker_with_utils';
export class DateEditor extends Component {
constructor(props) {
super(props)
this.onDateChange = this.onDateChange.bind(this)
this.state = {
value: null
}
}
componentDidMount() {
}
componentWillUnmount() {
}
componentDidUpdate() {
this.focus()
}
focus() {
window.setTimeout(() => {
let dateContainer = ReactDOM.findDOMNode(this.refs.dateContainer)
if (dateContainer) {
dateContainer.focus()
}
})
}
getValue() {
return this.state.value
}
isPopup() {
return false
}
onDateChange(date) {
this.setState({
value: date
},
() => this.props.api.stopEditing()
)
}
render() {
let storeValue = this.props.value
return (
<span
ref='dateContainer'
tabIndex={1}>
<SampleDatePickerWithUtils labelName={' '} schemaLocation='rowDate' isDisabled={false}
displayFormat='yyyy-mm-dd'
disableFuture={false}
onDateChange={this.onDateChange}
disablePast={false}
storeVal={storeValue}
gridSize={{ sm: 2, md: 1, lg: 1 }}></SampleDatePickerWithUtils>
</span>
)
}
}
SampleDatePickerWithUtils.js
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles'
import { updateFieldState, onTabOut, updateFocus } from 'actions/ticket_actions';
import { bindActionCreators } from 'redux';
import { getGridSizing } from 'styles/Sample_core_theme';
import { DatePicker, KeyboardDatePicker, MuiPickersUtilsProvider } from '#material-ui/pickers';
import { formatToYMD } from 'core/schema_translators/utils';
import EventIcon from '#material-ui/icons/Event';
import DateFnsUtils from '#date-io/date-fns'
import { SampleFieldComponent } from './base_components';
import { registerComponent } from 'actions/Sample_core_actions';
import { connect } from 'react-redux';
const moment = require('moment');
class MyDateFnsUtils extends DateFnsUtils {
startOfMonth(date) {
let dat = moment(date).startOf('month').toDate()
return dat
}
}
class _SampleDatePickerWithUtils extends SampleFieldComponent {
constructor(props) {
super(props);
let formattedDate = props.storeVal ? formatToYMD(props.storeVal) : null
this.state = {
value: formattedDate,
errorMsg: '',
isError: false,
}
this.styleProps = { disableUnderline: true }
this.inputLabelProps = { shrink: true }
this.onChangeCallback = this.onChangeCallback.bind(this)
// this.onBlurCallback = this.onBlurCallback.bind(this)
props.registerComponent(props.schemaLocation)
}
componentWillMount() {
this.props.registerComponent(this.props.schemaLocation)
if (this.props.manageValueManually) {
let dateValue = this.props.overriddenValue ? formatToYMD(this.props.overriddenValue) : null
this.props.updateFieldState(this.props.schemaLocation, dateValue)
}
}
componentDidMount() {
if (this.props.focusField) { this.focusDomElement() }
}
componentWillReceiveProps(nextProps) {
if (this.props.manageValueManually) {
if (nextProps.overriddenValue !== this.props.overriddenValue) {
let dateValue = nextProps.overriddenValue ? formatToYMD(nextProps.overriddenValue) : null
this.props.updateFieldState(this.props.schemaLocation, dateValue)
this.props.onTabOut(this.props.schemaLocation)
}
}
}
onChangeCallback(date) {
let formattedDate = date ? formatToYMD(date) : null
if (!this.props.manageValueManually) {
this.props.updateFieldState(this.props.schemaLocation, formattedDate)
this.props.onTabOut(this.props.schemaLocation) //this is required because the value is selected in a date picker
}
this.props.onDateChange(formattedDate)
}
render() {
const gridSizing = getGridSizing(this.props)
const { classes } = this.props
return (
<MuiPickersUtilsProvider utils={MyDateFnsUtils}>
<KeyboardDatePicker
keyboard={(!this.props.isDisabled).toString()}
keyboardIcon={<EventIcon style={{ fontSize: '22px', color: 'red' }} />}
clearable
disabled={this.props.isDisabled}
error={this.state.isError}
helperText={this.state.errorMsg}
InputProps={{ className: classes.inputProps }}
label={this.props.labelName === '' ? this.props.schemaLocation : this.props.labelName}
value='2020-04-30'
onChange={this.onChangeCallback}
// format={this.props.displayFormat}
format='yyyy-mm-dd'
onBlur={this.onBlurCallback}
InputLabelProps={this.inputLabelProps}
disableFuture={this.props.disableFuture}
disablePast={this.props.disablePast}
/>
</MuiPickersUtilsProvider>
);
}
}
const styles = (theme) => ({
inputProps: {
marginTop: '0px !important',
// fontSize: '14px',
border: 0,
'& input': {
fontSize: '14px',
'&:focus': {
boxSizing: 'content-box'
}
}
}
})
const SampleDatePickerWithUtils = withStyles(styles)(_SampleDatePickerWithUtils)
export { SampleDatePickerWithUtils }
Using KeyboardDatePicker with moment the format I use is
format = {moment.localeData().longDateFormat('L')} // In my case dd/mm/yyyy
If you want to use a different locale you can require it first and use it
require("moment/locale/en-us");
moment().locale("en-us");
format = {moment().locale(locale).localeData().longDateFormat('L')}
For the value I use moment date when the date has a value and null otherwise
value={date ? moment(date) : null}
and for the onChange I'm using universal format as I don't want to store it in locale format
{(date: any) => handleChange(path, date?.format('YYYY-MM-DD'))}
Related
I am trying to rearrange my MainHeader in Twilio. Trying to get rid of the Menu icon and the logo. Not sure if that is possible or not for the Menu icon but I tried hiding a Logo.
I tried following to hide the logo but it did not work.
import { VERSION } from '#twilio/flex-ui';
import { FlexPlugin } from '#twilio/flex-plugin';
import { ConnectorHelper } from './helpers/ConnectorHelper';
import ConnectorColorTheme from './theme/ConnectorColorTheme';
import reducers, { namespace } from './states';
import { StylesProvider, createGenerateClassName } from '#material-ui/core/styles';
import { FlexState } from '#twilio/flex-ui';
const PLUGIN_NAME = 'ConnectorPlugin';
const hideHeaderLogo = () => {
console.log("Hiding logo");
FlexState.ready().then(() => {
const { MainHeader } = FlexState;
MainHeader.setState({
showLogo: false
});
});
};
export default class ConnectorPlugin extends FlexPlugin {
constructor() {
super(PLUGIN_NAME);
}
async init(flex, manager) {
hideHeaderLogo();
const configuration = {
colorTheme: ConnectorColorTheme
};
apply theme
manager.updateConfig(configuration);
this.registerReducers(manager);
}
}
I have the enterprise version of ag-grid with the license installed and appears to be working properly, but my sidebar will not show up no matter what implementation I try from the code on the ag-grid site. The enterprise grouping is working correctly and all of the other data input is showing fine. The table does not change at all if sidebar is true or false or any other setting.
Currently I have:
import { RowGroupingModule } from '#ag-grid-enterprise/row-grouping';
import { AllModules } from "#ag-grid-enterprise/all-modules";
import { ClientSideRowModelModule } from "#ag-grid-community/client-side-row-model";
import { AgGridVue } from "#ag-grid-community/vue";
import '#ag-grid-community/all-modules/dist/styles/ag-grid.css';
import '#ag-grid-community/all-modules/dist/styles/ag-theme-material.css';
export default {
data() {
return {
columnDefs: null,
autoGroupColumnDef: null,
defaultColDef: null,
rowData: {test: "Loading..."},
rowSelection: null,
rowGroupPanelShow: null,
aggFuncs: null,
gridApi: null,
columnApi: null,
sideBar: null,
modules: [ClientSideRowModelModule, AllModules, RowGroupingModule],
}
},
methods: {
onGridReady(params) {
this.gridApi = params.api;
this.columnApi = params.columnApi;
},
components: {
AgGridVue,
},
created(){
this.sideBar = true;
},
}
<ag-grid-vue style="width: 100%; height: 100%;"
class="ag-theme-material"
:autoGroupColumnDef="autoGroupColumnDef"
:defaultColDef="defaultColDef"
:columnDefs="columnDefs"
:rowData="rowData"
:modules="modules"
:rowGroupPanelShow="rowGroupPanelShow"
:sideBar="sideBar"
:groupIncludeFooter="true"
:aggFuncs="aggFuncs"
rowSelection="single"
#grid-ready="onGridReady"
#column-row-group-changed="onColumnRowGroupChanged"
#row-selected="onRowSelected"
#firstDataRendered="onFirstDataRendered">
</ag-grid-vue>
I fixed this by using the following:
import { RowGroupingModule } from '#ag-grid-enterprise/row-grouping';
import { AllModules } from "#ag-grid-enterprise/all-modules";
import { MenuModule } from '#ag-grid-enterprise/menu';
import { SetFilterModule } from '#ag-grid-enterprise/set-filter';
import { ColumnsToolPanelModule } from '#ag-grid-enterprise/column-tool-panel';
import { FiltersToolPanelModule } from '#ag-grid-enterprise/filter-tool-panel';
import { ClientSideRowModelModule } from "#ag-grid-community/client-side-row-model";
import { AgGridVue } from "#ag-grid-community/vue";
import '#ag-grid-community/all-modules/dist/styles/ag-grid.css';
import '#ag-grid-community/all-modules/dist/styles/ag-theme-material.css';
export default {
data() {
return {
modules: [ClientSideRowModelModule, AllModules, RowGroupingModule, SetFilterModule, MenuModule, ColumnsToolPanelModule, FiltersToolPanelModule]
}
}
}
In ionic 4 or 5, tabbar is not hided on subpages.
Of course, it works well in ionic 2 or 3.
Please let me know how to solve this issue.
This is my solution.
But hope the best solution.
create TabsService
import this in app.module.ts
Here is full code of TabsService
import { Injectable } from '#angular/core';
import { filter } from 'rxjs/operators';
import { NavigationEnd, Router } from '#angular/router';
import { Platform } from '#ionic/angular';
#Injectable({
providedIn: 'root'
})
export class TabsService {
constructor(private router: Router, private platform: Platform) {
this.platform.ready().then(() => {
this.navEvents();
});
}
public hideTabs() {
const tabBar = document.getElementById('kidesiaTabBar');
if (tabBar && tabBar.style.display !== 'none') {
tabBar.style.display = 'none';
}
}
public showTabs() {
const tabBar = document.getElementById('kidesiaTabBar');
if (tabBar && tabBar.style.display !== 'flex') {
tabBar.style.display = 'flex';
}
}
private navEvents() {
this.router.events
.pipe(filter(e => e instanceof NavigationEnd))
.subscribe((e: any) => {
this.showHideTabs(e);
});
}
private showHideTabs(e: any) {
const urlArray = e.url.split('/');
if (urlArray.length >= 3) {
let shouldHide = true;
if (urlArray.length === 3 && urlArray[1] === 'tabs') {
shouldHide = false;
}
try {
setTimeout(() => (shouldHide ? this.hideTabs() : this.showTabs()), 300);
} catch (err) {}
}
}
}
Created an ag-grid custom floating filter with validation and need to style the filter when the validation fails with material-ui themed class using withStyles higher order component (HoC). However, when you wrap the custom floating filter with HoC onParentModelChanged doesn't get called at all.
Used hard-coded style to set the element style property but this is not allowed in the themed project using #material-ui/core v1.4.0.
Below is the custom floating filter that I created with validation and hard-coded style.
import { PropTypes } from 'prop-types';
import styles from './styles';
const DEFAULT_MIN_CHARS = 3;
const ENTER_KEY = 13;
export class CustomFloatingFilter extends Component {
constructor(props) {
super(props);
this.state = {
currentValue: '',
minChars: props.minChars ? props.minChars : DEFAULT_MIN_CHARS,
invalidFilter: false,
styles: styles()
};
this.keyPressed = this.keyPressed.bind(this);
this.valueChanged = this.valueChanged.bind(this);
this.onParentModelChanged = this.onParentModelChanged.bind(this);
this.onFloatingFilterChanged = change => {
const { minChars } = this.state;
if(change && change.model && (!change.model.filter || change.model.filter.length >= minChars)) {
return props.onFloatingFilterChanged(change);
}
return false;
};
}
keyPressed(event) {
this.setState(
{ enterPressed: event.which === ENTER_KEY },
() => {
const { enterPressed } = this.state;
if(enterPressed) { //update filter only when enter. valueChanged will handle all other cases
this.onFloatingFilterChanged(this.buildChanged());
}
}
);
}
valueChanged(event) {
const { minChars } = this.state;
this.setState(
{
currentValue: event.target.value,
invalidFilter: !!event.target.value && event.target.value.length < minChars
},
() => {
this.onFloatingFilterChanged(this.buildChanged());
}
);
}
onParentModelChanged(parentModel) {
this.setState({ currentValue: parentModel ? parentModel.filter : '' });
}
buildChanged() {
const { currentValue, enterPressed, invalidFilter } = this.state;
return {
model: {
filterType: 'text',
type: 'equals',
filter: currentValue
},
apply: !invalidFilter && (enterPressed || currentValue === '') //for applyButton:true case
};
}
render() {
const { currentValue, invalidFilter, styles } = this.state;
return (
<div style={invalidFilter ? styles.invalid : {} //eslint-disable-line
//withStyles HoC doesn't work with ag-grid floating filter component
}
>
<input className='ag-floating-filter-input' onKeyPress={this.keyPressed} onChange={this.valueChanged} value={currentValue} />
</div>
);
}
}
CustomFloatingFilter.propTypes = {
minChars: PropTypes.number,
onFloatingFilterChanged: PropTypes.func
};
export default CustomFloatingFilter;
new to Meteor and running into this issue. I am using Meteor 1.3.3
When I try to pass props from my parent Container to my React Component it keeps throwing an error I will post below.
Here is my React component Prospect.jsx:
import React from 'react'
import { createContainer } from 'meteor/react-meteor-data'
import { Residents } from '/collections/residents.jsx'
import ReactDOM from 'react-dom';
import RaisedButton from 'material-ui/RaisedButton';
// import '/collections/residents.jsx'
class Prospect extends React.Component {
render() {
return(
<div>
<h1>Prospect Resident - {this.props.prospect.name.first} </h1>
<RaisedButton label="Default" />
</div>
)
}
}
Prospect.propTypes = {
// prospect: React.PropTypes.object
}
export default createContainer((params) => {
const paramsId = params.params.prospectId
Meteor.subscribe('residents');
// Meteor.subscribe('resident');
prospect = Residents.find({_id: paramsId}).fetch()
console.log(prospect[0])
return {
prospect: prospect
}
}, Prospect)
and here is my Mongo collection
residents.jsx
import { Mongo } from 'meteor/mongo'
export const Residents = new Mongo.Collection('residents')
const nameSchema = new SimpleSchema({
first: {type: String},
last: {type: String}
})
const residentSchema = new SimpleSchema({
cId: { type: String },
name: { type: nameSchema },
status: { type: String },
})
Residents.attachSchema(residentSchema)
// METHODS
Meteor.methods({
'residents.insert'(resident) {
Residents.insert(resident)
}
})
// PUBLICATIONS
if(Meteor.isServer) {
Meteor.publish('residents', function() {
return Residents.find()
})
Meteor.publish('resident', function(id) {
return Residents.find({_id: id})
})
}
and here is my Route
FlowRouter.route('/prospects/:prospectId}', {
name: 'prospectShow',
action(params) {
mount(LoggedIn, { content:
<MuiThemeProvider muiTheme={getMuiTheme()}>
<Prospect params={{prospectId: params.prospectId}} />
</MuiThemeProvider>
})
}
So when I go to localhost:3000 route I get the error
Prospect.jsx:14Uncaught TypeError: Cannot read property 'name' of undefined
Exception from Tracker recompute function:
debug.js:41 TypeError: Cannot read property '_currentElement' of null
at ReactCompositeComponentWrapper._updateRenderedComponent (ReactCompositeComponent.js:772)
at ReactCompositeComponentWrapper._performComponentUpdate (ReactCompositeComponent.js:753)
at ReactCompositeComponentWrapper.updateComponent (ReactCompositeComponent.js:672)
at ReactCompositeComponentWrapper.receiveComponent (ReactCompositeComponent.js:571)
at Object.receiveComponent (ReactReconciler.js:127)
at ReactCompositeComponentWrapper._updateRenderedComponent (ReactCompositeComponent.js:775)
at ReactCompositeComponentWrapper._performComponentUpdate (ReactCompositeComponent.js:753)
at ReactCompositeComponentWrapper.updateComponent (ReactCompositeComponent.js:672)
at ReactCompositeComponentWrapper.performUpdateIfNecessary (ReactCompositeComponent.js:585)
at Object.performUpdateIfNecessary (ReactReconciler.js:160)
My console.log(prospect[0]) in the container returns the object just fine, and it also works if I pass it in like this
return {
prospect: {name: {first: 'Joe', last: 'Smith'}}
}
So it's something about the returned object I think. Any help would be greatly appreciated, thanks
I ended up going with a solution like this. If anyone wants to answer and explain why this is needed (I thought in meteor 1.3 this wasn't needed anymore) I will accept your answer.
import React from 'react'
import { createContainer } from 'meteor/react-meteor-data'
import { Residents } from '/collections/residents.jsx'
class Prospect extends React.Component {
render() {
if(!this.props.ready){return <span>Loading...</span>}
const { prospect } = this.props
return(
<div>
<h1>{prospect.name.first} {prospect.name.last}</h1>
<div>Company: <b>{prospect.cId}</b></div>
</div>
)
}
}
Prospect.propTypes = {
ready: React.PropTypes.bool.isRequired,
prospect: React.PropTypes.object.isRequired
}
export default createContainer((params) => {
return {
ready: Meteor.subscribe('resident', params.id).ready(),
prospect: Residents.findOne(params.id)
}
}, Prospect)