How to properly implement toast-ui/calendar in nextjs - next.js

I am trying to implement #toast-ui/react-calendar, initially I was getting window is not defined but after implementing the fix I got here https://github.com/nhn/toast-ui.react-calendar/issues/39, I got this instead Unhandled Runtime Error Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports. Check the render method of __WEBPACK_DEFAULT_EXPORT__.
This is my curent code
CalendarPage.js
import React from 'react';
import Calendar from '#toast-ui/react-calendar';
import 'tui-calendar/dist/tui-calendar.css';
import 'tui-date-picker/dist/tui-date-picker.css';
import 'tui-time-picker/dist/tui-time-picker.css';
export default (props) => <Calendar {...props} ref={props.forwardedRef} />;
schedule>index.jsx
import { forwardRef, useCallback, useRef, useState } from 'react';
import dynamic from 'next/dynamic';
const TuiCalendar = dynamic(() => import('#components/calendars/CalendarPage'), { ssr: false });
const CalendarWithForwardedRef = forwardRef((props, ref) => (
<TuiCalendar {...props} forwardedRef={ref} />
));
const start = new Date();
const end = new Date(new Date().setMinutes(start.getMinutes() + 30));
const schedules = [
{
calendarId: '1',
category: 'time',
isVisible: true,
title: 'Study',
id: '1',
body: 'Test',
start,
end,
},
{
calendarId: '2',
category: 'time',
isVisible: true,
title: 'Meeting',
id: '2',
body: 'Description',
start: new Date(new Date().setHours(start.getHours() + 1)),
end: new Date(new Date().setHours(start.getHours() + 2)),
},
];
const calendars = [
{
id: '1',
name: 'My Calendar',
color: '#ffffff',
bgColor: '#9e5fff',
dragBgColor: '#9e5fff',
borderColor: '#9e5fff',
},
{
id: '2',
name: 'Company',
color: '#ffffff',
bgColor: '#00a9ff',
dragBgColor: '#00a9ff',
borderColor: '#00a9ff',
},
];
const SchedulePage = () => {
const cal = useRef(null);
const onClickSchedule = useCallback((e) => {
const { calendarId, id } = e.schedule;
const el = cal.current.calendarInst.getElement(id, calendarId);
console.log(e, el.getBoundingClientRect());
}, []);
const onBeforeCreateSchedule = useCallback((scheduleData) => {
console.log(scheduleData);
const schedule = {
id: String(Math.random()),
title: scheduleData.title,
isAllDay: scheduleData.isAllDay,
start: scheduleData.start,
end: scheduleData.end,
category: scheduleData.isAllDay ? 'allday' : 'time',
dueDateClass: '',
location: scheduleData.location,
raw: {
class: scheduleData.raw['class'],
},
state: scheduleData.state,
};
cal.current.calendarInst.createSchedules([schedule]);
}, []);
const onBeforeDeleteSchedule = useCallback((res) => {
console.log(res);
const { id, calendarId } = res.schedule;
cal.current.calendarInst.deleteSchedule(id, calendarId);
}, []);
const onBeforeUpdateSchedule = useCallback((e) => {
console.log(e);
const { schedule, changes } = e;
cal.current.calendarInst.updateSchedule(schedule.id, schedule.calendarId, changes);
}, []);
function _getFormattedTime(time) {
const date = new Date(time);
const h = date.getHours();
const m = date.getMinutes();
return `${h}:${m}`;
}
function _getTimeTemplate(schedule, isAllDay) {
var html = [];
if (!isAllDay) {
html.push('<strong>' + _getFormattedTime(schedule.start) + '</strong> ');
}
if (schedule.isPrivate) {
html.push('<span class="calendar-font-icon ic-lock-b"></span>');
html.push(' Private');
} else {
if (schedule.isReadOnly) {
html.push('<span class="calendar-font-icon ic-readonly-b"></span>');
} else if (schedule.recurrenceRule) {
html.push('<span class="calendar-font-icon ic-repeat-b"></span>');
} else if (schedule.attendees.length) {
html.push('<span class="calendar-font-icon ic-user-b"></span>');
} else if (schedule.location) {
html.push('<span class="calendar-font-icon ic-location-b"></span>');
}
html.push(' ' + schedule.title);
}
return html.join('');
}
const templates = {
time: function (schedule) {
console.log(schedule);
return _getTimeTemplate(schedule, false);
},
};
return (
<div className='App'>
<h1>Welcome to TOAST Ui Calendar</h1>
<CalendarWithForwardedRef
ref={cal}
height='1000px'
useCreationPopup={true}
useDetailPopup={true}
calendars={calendars}
schedules={schedules}
onClickSchedule={onClickSchedule}
onBeforeCreateSchedule={onBeforeCreateSchedule}
onBeforeDeleteSchedule={onBeforeDeleteSchedule}
onBeforeUpdateSchedule={onBeforeUpdateSchedule}></CalendarWithForwardedRef>
</div>
);
};
export default SchedulePage;
I do not what I am doing wrong here but I keep getting this error
Unhandled Runtime Error
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check the render method of `__WEBPACK_DEFAULT_EXPORT__`.
Call Stack
createFiberFromTypeAndProps
node_modules\react-dom\cjs\react-dom.development.js (25058:0)
createFiberFromElement
node_modules\react-dom\cjs\react-dom.development.js (25086:0)
reconcileSingleElement
node_modules\react-dom\cjs\react-dom.development.js (14052:0)
reconcileChildFibers
node_modules\react-dom\cjs\react-dom.development.js (14112:0)

Turns out that the issue with my code was caused by clashing npm packages, I had react-big-calendar installed previously removing that fixed the issue

Related

localStorage.getItem is not working on nextjs, using redux and material ui

i am trying to save user settings, [dark mode/ light mode] on local storage using redux on nextjs
I can save the data on the local storage but i can't pull the data into the initialSatte. here is my code
import { createSlice } from "#reduxjs/toolkit";
import Cookies from "js-cookie";
const getFromLocalStorage = (key: string) => {
if (!key || typeof window === "undefined") {
return "";
}
try {
// #ts-ignore
return JSON.parse(localStorage.getItem(key)) || {};
} catch (error) {
return {};
}
};
export const uiSettings = createSlice({
name: "uiSettings",
initialState: {
theme: getFromLocalStorage("uiSettings")?.theme || "dark",
},
reducers: {
themeSwitch: (state, action) => {
state.theme = action.payload;
window.localStorage.setItem(
"uiSettings",
JSON.stringify({
theme: action.payload,
})
);
},
},
extraReducers: (builder) => {},
});
export const uiSettingsReducer = uiSettings.reducer;
export const { themeSwitch } = uiSettings.actions;
i tried alot of ways to solve it but it didn't work.

Cant get the SQLite table to be shown through RecyclerListView in React-Native

I'm pretty new to React native, I'm trying to create a dictionary app and in order to that, wanna fetch some data from a local SQLite and put it to RecyclerListView, But I can not get any results, just empty white screen. What am I doing wrong??
Here is my code:
import React, {Component} from 'react';
import {StyleSheet , View, Dimensions , Text , Image} from 'react-native';
import { RecyclerListView, DataProvider , LayoutProvider } from 'recyclerlistview';
const SCREEN_WIDTH = Dimensions.get('window').width;
var SQLite = require('react-native-sqlite-storage')
var db = SQLite.openDatabase({name: 'tes.sqlite', createFromLocation: '~dictionary.sqlite'})
export default class App extends Component {
constructor(props){
super(props);
const record = [];
db.transaction((tx) => {
tx.executeSql('SELECT * FROM tblWord', [], (tx, results) => {
// var len=results.rows.length;
for (let i = 0; i < 10 ; i++) {
let row = results.rows.item(i);
record.push({
type: 'NORMAL',
item:{
id:row.id,
name: row.word_english,
description: row.word_persian,}
});
console.log('row.word_english = ' + row.word_english ) //this shows the right output in console
}
});
});
this.state = {
list: new DataProvider((r1, r2) => r1 !== r2).cloneWithRows(record),
};
this.layoutProvider = new LayoutProvider((i) => {
return this.state.list.getDataForIndex(i).type;
}, (type ,dim) => {
switch (type) {
case 'NORMAL' :
dim.width = SCREEN_WIDTH;
dim.height=100;
break;
default:
dim.width = 0;
dim.height = 0;
break;
}
})
}
rowRenderer = (type , data) => {
const {name , description } = data.item;
return (
<View>
<Text style={{fontSize:20 , color : 'blue'}}>name: {name}</Text>
<Text>desc: {description}</Text>
</View>
)
}
render() {
return(
<View style={styles.container}>
<RecyclerListView
style = {{flex:1}}
rowRenderer={this.rowRenderer}
dataProvider={this.state.list}
layoutProvider={this.layoutProvider}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container:{
flex:1,
},
})
PS: If I replace the transaction function with the code below, it works perfectly:
for (i=0; i<10 ; i+=1) {
record.push({
type: 'NORMAL',
item:{
id:1,
name: 'name1',
description: 'this is for testing.',
},
});
}
try this:
db.transaction(tx => {
tx.executeSql('SELECT * FROM tblWord',
[], (_, { rows: { _array } }) => {console.log(_array)})
})

Redux immutable pattern

I use react with redux.
Action:
export const updateClicked = (id, section) => {
return {
type: actionTypes.UPDATE_CLICKED,
id,
section
};
};
Please advise the best way to immutable update property in nested array:
Reducer:
const initialState = {
updates: {
html: {
id: 'html',
label: 'HTML',
count: 0,
items: [
{
id: 1,
label: 'Header',
price: 10,
bought: false
},
{
id: 2,
label: 'Sidebar',
price: 50,
bought: false
}
]
}
}
};
My action:
action = {
id: 1,
bought: true
}
I want to update bought property inside items array. I.e.:
const updateClicked= (state, action) => {
const updateSections = state.updates[action.section].items;
const updatedItems = updateSections.map(el => {
if (el.id === action.id && !el.bought) {
el.bought = true;
}
return el;
});
//How to update state???
return {}
};
Will be glad if you explain 2 ways to do this:
With es6 spread operator
With some library (like immutability-helper)
Thanks!
With es6 spread operator:
export default (state = initialState, action) => {
if (action.type !== actionTypes.UPDATE_CLICKED) return state;
return {
...state,
updates: {
...state.updates,
html: {
...state.updates.html,
items: state.updates.html.items.map((item, idx) => idx === action.id
? {...item, bought: item.bought}
: item
)
}
}
}
};

How to test graphql apollo component? How to get complete coverage inside compose()?

I'm having issues getting complete coverage on my apollo components. istanbul is reporting the functions inside compose() are not getting called. These are Redux connect() functions and apollo graph() functions.
export default compose (
...
connect(mapStateToProps, mapDispatchToProps), // <-- functions not covered
graphql(builderQuery, {
options: (ownProps) => { // <-- function not covered
...
)(ComponentName);
I'm mounting using enzyme, trying to do something similar to the react-apollo example.
const mounted = shallow(
<MockedProvider mocks={[
{ request: { query, variables }, result: { data: response.data } }
]}>
<ConnectedComponentName />
</MockedProvider>
);
The only way I've been able to achieve 100% coverage is if I export all of the functions and call them directly.
testing composed graphql/redux containers
Try something like this for your setup:
// mocks.js
import configureMockStore from 'redux-mock-store'
import { ApolloClient } from 'react-apollo'
import { mockNetworkInterface } from 'apollo-test-utils'
export const mockApolloClient = new ApolloClient({
networkInterface: mockNetworkInterface(),
})
export const createMockStore = configureMockStore()
This will set you up to properly test your containers:
// container-test.js
import { mount } from 'enzyme'
import { createMockStore, mockApolloClient } from 'mocks'
beforeEach(() => {
store = createMockStore(initialState)
wrapper = mount(
<ApolloProvider client={mockApolloClient} store={store}>
<Container />
</ApolloProvider>
)
})
it('maps state & dispatch to props', () => {
const props = wrapper.find('SearchResults').props()
const expected = expect.arrayContaining([
// These props come from an HOC returning my grapqhql composition
'selectedListing',
'selectedPin',
'pathname',
'query',
'bbox',
'pageNumber',
'locationSlug',
'selectListing',
'updateCriteria',
'selectPin',
])
const actual = Object.keys(props)
expect(actual).toEqual(expected)
})
testing graphql options
Because the graphql fn has a signature like graphql(query, config), you can export your config for testing in isolation for more granular coverage.
import { config } from '../someQuery/config'
describe('config.options', () => {
const state = {
bbox: [],
locationSlug: 'foo',
priceRange: 'bar',
refinements: 'baz',
userSort: 'buzz',
}
const results = {
points: [
{ propertyName: 'Foo' },
{ propertyName: 'Bar' },
],
properties: [
{ propertyName: 'Foo' },
{ propertyName: 'Bar' },
],
}
it('maps input to variables', () => {
const { variables } = config.options(state)
const expected = { bbox: [], locationSlug: 'foo', priceRange: 'bar', refinements: 'baz', userSort: 'buzz' }
expect(variables).toEqual(expected)
})
it('returns props', () => {
const response = { data: { loading: false, geo: { results } } }
const props = config.props(response)
expect(props.results).toEqual(results.properties)
expect(props.spotlightPoints).toEqual(results.points)
})
})

meteor react-autosuggest - Missing class properties transform

I'm using Meteor React and trying to get react-autosuggest working (from the basic usage example https://github.com/moroshko/react-autosuggest
The error I get is Missing class properties transform - it's happening at onChange (right after this.state.
this.state = {
value: '',
suggestions: getSuggestions('')
};
}
onChange = (event, { newValue }) => {
this.setState({
value: newValue
});
};
onSuggestionsUpdateRequested = ({ value }) => {
this.setState({
suggestions: getSuggestions(value)
});
};
I've looked around to see if I could find a solution, but no luck.
Hopefully someone can shed some light on what's going on.
Using class Example extends React.Component {...} doesn't work with meteor. Try this approach instead:
import React from 'react';
import Autosuggest from 'react-autosuggest';
import AutosuggestHighlight from 'autosuggest-highlight';
Example = React.createClass({
getInitialState() {
return {
value: '',
suggestions: [],
};
},
onChange(event, { newValue }) {
this.setState({
value: newValue,
});
},
onSuggestionsFetchRequested({ value }) {
this.setState({
suggestions: this.getSuggestions(value),
});
},
onSuggestionsClearRequested() {
this.setState({
suggestions: [],
});
},
getSuggestionValue(suggestion) {
return suggestion.name;
},
getSuggestions(value) {
const languages = [
{
name: 'C',
year: 1973,
},
{
name: 'C#',
year: 2001,
},
{
name: 'C++',
year: 1984,
},
{
name: 'Clojure',
year: 2008,
},
{
name: 'Elm',
year: 2013,
},
{
name: 'Go',
year: 2010,
},
];
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
if (inputLength === 0) {
return [];
}
return languages.filter(language =>
language.name.toLowerCase().slice(0, inputLength) === inputValue
);
},
renderSuggestion(suggestion, { query }) {
const suggestionText = `${suggestion.name} (${suggestion.year})`;
const matches = AutosuggestHighlight.match(suggestionText, query);
const parts = AutosuggestHighlight.parse(suggestionText, matches);
return (
<span className="suggestion-content">
<span>
{
parts.map((part, index) => {
const className = part.highlight ? 'highlight' : null;
return (
<span className={className} key={index}>{part.text}</span>
);
})
}
</span>
</span>
);
},
render() {
const { value, suggestions } = this.state;
const inputProps = {
value,
placeholder: 'Enter programming language...',
onChange: this.onChange,
};
return (
<div>
<Autosuggest
suggestions={suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
getSuggestionValue={this.getSuggestionValue}
renderSuggestion={this.renderSuggestion}
inputProps={inputProps} />
</div>
);
},
});
Then simply use <Example /> to render the autosuggest field.
This was tested with Meteor 1.4.1.1

Resources