Full calendar - Can not refresh events in react - fullcalendar

I use Full Calendar in my React project. I can not refresh the calendar although I make an update on the state.
Here is my sample code: https://codesandbox.io/embed/fullcalendar-react-jp7n1
In this sample, I change the title of events when user clicks on the Change title button, but nothing changes. I there anything that I miss?
Thanks for your help.
import React from "react";
import FullCalendar from "#fullcalendar/react";
import dayGridPlugin from "#fullcalendar/daygrid";
import timeGridPlugin from "#fullcalendar/timegrid";
import interactionPlugin from "#fullcalendar/interaction";
import "./styles.css";
import "#fullcalendar/core/main.css";
import "#fullcalendar/daygrid/main.css";
import "#fullcalendar/timegrid/main.css";
export default class DemoApp extends React.Component {
calendarComponentRef = React.createRef();
state = {
calendarWeekends: true,
calendarEvents: [
// initial event data
{ title: "Event 1", start: new Date() },
{ title: "Event 2", start: new Date() },
{ title: "Event 3", start: new Date() }
]
};
render() {
return (
<div className="demo-app">
<div className="demo-app-top">
<button onClick={this.changeTitle}>Change title</button>
</div>
<div className="demo-app-calendar">
<FullCalendar
defaultView="dayGridMonth"
header={{
left: "prev,next today",
center: "title",
right: "dayGridMonth,timeGridWeek,timeGridDay,listWeek"
}}
plugins={[dayGridPlugin, timeGridPlugin, interactionPlugin]}
ref={this.calendarComponentRef}
weekends={this.state.calendarWeekends}
events={this.state.calendarEvents}
/>
</div>
</div>
);
}
changeTitle = () => {
let events = [...this.state.calendarEvents];
events[0].title = new Date().toTimeString();
events[1].title = new Date().toTimeString();
this.setState({ calendarEvents: events });
};
}

Full calendar uses deep equality check to test for changes and since the object of the event inside the array is not changed (it's the same object by reference) it doesn't detect the change. You need to create a new event object to update it.
let events = [ ...this.state.calendarEvents];
events[0] = {
title: new Date().toTimeString(),
start: events[0] .start
}
events[1] = {
title: new Date().toTimeString(),
start: events[1].start
}
this.setState({ calendarEvents: events});

Related

Storybook Vue3 - Work with v-model in stories

I have a question regarding Storybook and Vue components with v-models. When writing a story for let's say an input component with a v-model i want a control reflecting the value of this v-model. Setting the modelValue from the control is no problem, but when using the component itself the control value stays the same. I am searching the web for a while now but i can't seem to find a solution for this.
A small example:
// InputComponent.vue
<template>
<input
type="text"
:value="modelValue"
#input="updateValue"
:class="`form-control${readonly ? '-plaintext' : ''}`"
:readonly="readonly"
/>
</template>
<script lang="ts">
export default {
name: "GcInputText"
}
</script>
<script lang="ts" setup>
defineProps({
modelValue: {
type: String,
default: null
},
readonly: {
type: Boolean,
default: false
}
});
const emit = defineEmits(['update:modelValue']);
const updateValue = (event: Event) => {
const target = event.target as HTMLInputElement;
emit('update:modelValue', target.value);
}
</script>
In Storybook:
Does anyone have a solution to make this working?
Thanks in advance!
In my case, I have a custom select input that uses a modelValue prop.
I tried this and worked for me:
at my-component.stories.js:
import { ref } from 'vue'
import MyComponent from './MyComponent.vue'
export default {
title: 'Core/MyComponent',
component: MyComponent,
argTypes: { }
}
const Template = (args) => ({
components: { MyComponent },
setup() {
let model = ref('Javascript')
const updateModel = (event) => model.value = event
return { args, model, updateModel }
},
template: '<my-component v-bind="args" :modelValue="model" #update:modelValue="updateModel" />'
})
export const Default = Template.bind({})
Default.args = {
options: [
'Javascript',
'PHP',
'Java'
]
}

Openlayer tiles do not render on build

I am using ol version 7.1 with react 18.04 and next 13
This is the code
import { Feature } from "ol";
import { Point } from "ol/geom";
import TileLayer from "ol/layer/Tile";
import VectorLayer from "ol/layer/Vector";
import Map from "ol/Map";
import "ol/ol.css";
import { fromLonLat } from "ol/proj";
import OSM from "ol/source/OSM";
import VectorSource from "ol/source/Vector";
import Icon from "ol/style/Icon";
import Style from "ol/style/Style";
import View from "ol/View";
import type { FunctionComponent, PropsWithChildren } from "react";
import { useEffect, useRef } from "react";
export const Map: FunctionComponent<
PropsWithChildren<{ coordinates: number[] }>
> = ({ children, coordinates }) => {
const transformedCoordinates = fromLonLat(coordinates);
const mapElement = useRef<HTMLDivElement>(null);
const mapRef = useRef<Map>();
useEffect(() => {
const point = new Point(transformedCoordinates);
const feature = new Feature({ geometry: point });
const drawSource = new VectorSource({ wrapX: false, features: [feature] });
const svg = 'some svg'
const style = new Style({
image: new Icon({
opacity: 1,
src,
scale: 0.08,
}),
});
if (mapElement.current && !mapRef.current) {
mapRef.current = new Map({
target: mapElement.current ?? undefined,
layers: [
new TileLayer({
source: new OSM(),
}),
new VectorLayer({
source: drawSource,
style,
}),
],
view: new View({
center: transformedCoordinates,
zoom: 14,
}),
});
}
}, [transformedCoordinates]);
return (
<div
id="map"
className="map"
style={{ height: "100%", width: "100%" }}
ref={mapElement}
>
{children}
</div>
);
};
Used this way:
<div className="relative h-[250px] w-[350px]">
<Map coordinates={data} />
</div>
Using next dev is rendering the map totally fine:
But using next build, does not display the map, but just the outer style. What am I missing?
I tried adding some Scripts which I saw from ol, but the threads were pretty old and it did not change anything.
I do not know where ol gets the map styling etc. from. It seems like somehow this gets missing in build mode?
I am also using JOY ui & tailwind css, not sure if there are classnames which could override something in ol?
Try setting swcMinify to false in next.config. Since Next v13 swcMinify is true by default.

CodeMirror on Vue3 has a problem when setValue is kicked

I'm trying to use CodeMirror on Vue3 and the problem occurs when I call doc.setValue().
The Problem is following:
Cursor position is broken when doc.setValue() is called
CodeMirror throws an exception when continuing editing
The exception is here.
Uncaught TypeError: Cannot read property 'height' of undefined
at lineLength (codemirror.js:1653)
at codemirror.js:5459
at LeafChunk.iterN (codemirror.js:5623)
at Doc.iterN (codemirror.js:5725)
at Doc.iter (codemirror.js:6111)
at makeChangeSingleDocInEditor (codemirror.js:5458)
at makeChangeSingleDoc (codemirror.js:5428)
at makeChangeInner (codemirror.js:5297)
at makeChange (codemirror.js:5288)
at replaceRange (codemirror.js:5502)
How should I solve this?
~~~
Versions are CodeMirror: 5.61.1, Vue.js: 3.0.11
My code is following:
index.html
<div id="app"></div>
<script src="./index.js"></script>
index.js
import { createApp } from 'vue';
import App from './App';
const app = createApp(App);
app.mount('#app');
App.vue
<template>
<div>
<button #click="click">Push Me</button>
<textarea id="codemirror"></textarea>
</div>
</template>
<script>
import CodeMirror from 'codemirror/lib/codemirror.js';
import 'codemirror/lib/codemirror.css';
// import codemirror resources
import 'codemirror/addon/mode/overlay.js';
import 'codemirror/mode/markdown/markdown.js';
import 'codemirror/mode/gfm/gfm.js';
export default {
data () {
return {
cm: null
}
},
mounted () {
this.cm = CodeMirror.fromTextArea(document.getElementById('codemirror'), {
mode: 'gfm',
lineNumbers: true,
});
},
methods: {
click (event) {
this.cm.getDoc().setValue('foo\nbar');
}
}
}
</script>
Thanks.
UPDATES
First, this problem also occurs when I used replaceRange() with multiline.
Unfortunately, I couldn't find any solution. So I tried to find another way.
My solution is recreating Codemirror instance with a textarea that has new content.
It works well.
// Remove old editor
this.cm.toTextArea();
// Get textarea
const textarea = document.getElementById('codemirror');
// Set new content
textarea.value = 'foo\nbar';
// Create new editor
this.cm = CodeMirror.fromTextArea(textarea, { /** options */ });
I found a method, you can use toRaw to get the original Object from Proxy,and this method can be also used in monaco-editor
import { toRaw } from 'vue'
import CodeMirror from 'codemirror/lib/codemirror.js';
import 'codemirror/lib/codemirror.css';
// import codemirror resources
import 'codemirror/addon/mode/overlay.js';
import 'codemirror/mode/markdown/markdown.js';
import 'codemirror/mode/gfm/gfm.js';
export default {
data () {
return {
cm: null
}
},
mounted () {
this.cm = CodeMirror.fromTextArea(document.getElementById('codemirror'), {
mode: 'gfm',
lineNumbers: true,
});
},
methods: {
click (event) {
toRaw(this.cm).setValue('foo\nbar');
}
}
}
Another way,you don't have to define cm in data, just use this.cm
data () {
return {
//cm: null
}
},

change the first day of the week with work week view

I'm trying to change the first day of the work_week view with react big calendar but I didn't found a way to do it... I was able to change the first day of the week view but nothing helped with work week... any ideas?
This is how I changed the first day of the week view:
import "moment/locale/en-gb";
moment.locale("en-gb", {
week: {
dow: 0,
},
});
const localizer = momentLocalizer(moment);
Unfortunately, the only way to do this is by defining a custom 'View', rather than using the 'work_week' view. First you can create the 'View', using the 'work_week' view as a template.
import PropTypes from "prop-types";
import React from "react";
import Week from "react-big-calendar/lib/Week";
import TimeGrid from "react-big-calendar/lib/TimeGrid";
function workWeekRange(date, options) {
return Week.range(date, options).filter(
(d) => [0, 1, 6].indexOf(d.getDay()) === -1
);
}
class MyWorkWeek extends React.Component {
render() {
let { date, ...props } = this.props;
let range = workWeekRange(date, this.props);
return <TimeGrid {...props} range={range} eventOffset={15} />;
}
}
MyWorkWeek.propTypes = {
date: PropTypes.instanceOf(Date).isRequired
};
MyWorkWeek.defaultProps = TimeGrid.defaultProps;
MyWorkWeek.range = workWeekRange;
MyWorkWeek.navigate = Week.navigate;
MyWorkWeek.title = (date, { localizer }) => {
let [start, ...rest] = workWeekRange(date, { localizer });
return localizer.format({ start, end: rest.pop() }, "dayRangeHeaderFormat");
};
export default MyWorkWeek;
The magic there is in the workWeekRange method, where I defined the days (zero index) to hide from the week. In this example I'm hiding Sunday, Monday and Saturday.
Also notice that I had to change my import statements for the Week and TimeGrid components.
The last thing I had to do was provide the custom 'View', as well as a title for the button in the Toolbar. You do this in your calendar props. The views prop changes from the standard array to an object.
return (
<Calendar
// other props
views={{
day: true,
month: true,
myweek: MyWorkWeek
}}
messages={{
myweek: 'Work Week'
}}
/>
You can see a working example in CodeSandbox.

Force state eg checked="true" for checkbox in Storybook?

I have custom styled checkbox. Is there a way in Storybook to make the checkbox checked so that you dont have to click on it to see this state?
This is a work in progress, not fully functioning yet but it does pass props so I am just getting storybook to cooperate.
the storybook-addon-pseudo-states really should have this built in...
I am using a jsx approach but the difference is negligible.
I just started learning Vue and storybook last night, so this is quite new to me, I will post up a fully functional version later.
Checkbox.stories.js
export const CheckBoxChecked = Template.bind({});
CheckBoxChecked.args = {
label: 'Checked',
checked: true,
};
Checkbox.jsx
import Vue from 'vue';
import { styled, VueEmotion } from '#egoist/vue-emotion';
import './checkbox.css';
Vue.use(VueEmotion);
const Label = styled('label')`
`
const Input = styled('input')`
opacity: 0;
width: 1em;
height: 1em;
`
export default {
name: 'CheckBox',
props: {
label: {
type: String,
required: true,
},
checked: {
type: Boolean,
default: false
},
render (h) {
return (
<Label>
<Input type="checkbox" checked={this.checked} />
</Label>
)
}
}

Resources