Created
October 17, 2020 02:42
-
-
Save lmiller1990/bca97f1a32e5878ea1652a4b594d3ab1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<h3>Luxon</h3> | |
<date-time | |
v-model="dateLuxon" | |
:deserialize="deserialize" | |
:serialize="serialize" | |
/> | |
{{ dateLuxon }} | |
<h3>Moment</h3> | |
<date-time | |
v-model="dateMoment" | |
:deserialize="deserializeMoment" | |
:serialize="serializeMoment" | |
/> | |
{{ dateMoment.format ? dateMoment.format('YYYY-MM-DD') : dateMoment }} | |
<hr /> | |
</template> | |
<script> | |
import { ref } from 'vue' | |
import { DateTime } from 'luxon' | |
import { | |
serialize, | |
deserialize, | |
serializeMoment, | |
deserializeMoment | |
} from './date-time-serializers.js' | |
import moment from 'moment' | |
import dateTime from './date-time.vue' | |
export default { | |
components: { dateTime }, | |
setup() { | |
const dateLuxon = ref(DateTime.fromObject({ | |
year: '2019', | |
month: '01', | |
day: '01', | |
})) | |
const dateMoment = ref(moment('2019-02-02', 'YYYY-MM-DD')) | |
return { | |
dateLuxon, | |
serialize, | |
deserialize, | |
dateMoment, | |
serializeMoment, | |
deserializeMoment | |
} | |
} | |
} | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { DateTime } from 'luxon' | |
import moment from 'moment' | |
export function serializeMoment(value) { | |
const toString = `${value.year}-${value.month.padStart(2, '0')}-${value.day.padStart(2, '0')}` | |
const toObject = moment(toString, 'YYYY-MM-DD', true) | |
if (toObject.isValid()) { | |
return toObject | |
} | |
return | |
} | |
export function deserializeMoment(value) { | |
if (!moment.isMoment(value)) { | |
return value | |
} | |
return { | |
year: value.year().toString(), | |
month: (value.month() + 1).toString(), | |
day: value.date().toString() | |
} | |
} | |
export function deserialize(value) { | |
return { | |
year: value.get('year'), | |
month: value.get('month'), | |
day: value.get('day') | |
} | |
} | |
export function serialize(value) { | |
try { | |
const obj = DateTime.fromObject(value) | |
if (obj.invalid) { | |
return | |
} | |
} catch { | |
return | |
} | |
return DateTime.fromObject(value) | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { mount } from '@vue/test-utils' | |
import moment from 'moment' | |
import { DateTime } from 'luxon' | |
import dateTime from './date-time.vue' | |
import { | |
serialize, | |
deserialize, | |
serializeMoment, | |
deserializeMoment | |
} from './date-time-serializers.js' | |
describe('serializeMoment', () => { | |
it('serializes valid moment', () => { | |
const actual = serializeMoment({ year: '2020', month: '1', day: '1' }) | |
// compare as strings. moment is pain. | |
expect(actual.toString()).toEqual(moment('2020-01-01').toString()) | |
}) | |
it('returns undefined for invalid moment', () => { | |
const actual = serializeMoment({ year: '200000020', month: '1xxxxx', day: 'bbbb' }) | |
expect(actual).toEqual(undefined) | |
}) | |
}) | |
describe('deserialize', () => { | |
it('deserializes to Luxon DateTime', () => { | |
const actual = deserialize(DateTime.fromObject({ year: '2020', month: '1', day: '1' })) | |
expect(actual).toEqual({ year: 2020, month: 1, day: 1 }) | |
}) | |
}) | |
describe('serialize', () => { | |
it('serializes valid Luxon DateTime', () => { | |
const actual = serialize({ year: '2020', month: '1', day: '1' }) | |
expect(actual).toEqual(DateTime.fromObject({ year: 2020, month: 1, day: 1 })) | |
}) | |
it('returns undefined for invalid Luxon DateTime', () => { | |
const actual = serialize({ year: '200000020', month: '1xxxxx', day: '1' }) | |
expect(actual).toEqual(undefined) | |
}) | |
}) | |
describe('deserialize', () => { | |
it('deserializes to Luxon DateTime', () => { | |
const actual = deserialize(DateTime.fromObject({ year: '2020', month: '1', day: '1' })) | |
expect(actual).toEqual({ year: 2020, month: 1, day: 1 }) | |
}) | |
}) | |
test('DateTime', async () => { | |
const wrapper = mount(dateTime, { | |
props: { | |
modelValue: DateTime.fromObject({ year: '2020', month: '1', day: '1' }), | |
serialize, | |
deserialize | |
} | |
}) | |
await wrapper.find('[data-test-year]').setValue('2019') | |
await wrapper.find('[data-test-month]').setValue('2') | |
await wrapper.find('[data-test-day]').setValue('3') | |
// 3 successful updates, 3 emits. | |
expect(wrapper.emitted('update:modelValue')).toHaveLength(3) | |
// update:modelValue will not update the modelValue prop | |
// in Vue Test Utils, though. | |
// we could wrap this in another component and do something | |
// fancy but it's not really worth it. I think this is fine, | |
// since we know the limitations and understand why we are doing | |
// what we are doing here. | |
expect(wrapper.emitted('update:modelValue')[0][0]).toEqual( | |
DateTime.fromObject({ year: '2019', month: '1', day: '1' }) | |
) | |
expect(wrapper.emitted('update:modelValue')[1][0]).toEqual( | |
DateTime.fromObject({ year: '2020', month: '2', day: '1' }) | |
) | |
expect(wrapper.emitted('update:modelValue')[2][0]).toEqual( | |
DateTime.fromObject({ year: '2020', month: '1', day: '3' }) | |
) | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<input data-test-year :value="date.year" @input="update($event, 'year')" /> | |
<input data-test-month :value="date.month" @input="update($event, 'month')" /> | |
<input data-test-day :value="date.day" @input="update($event, 'day')" /> | |
<pre> | |
date is: | |
{{ date }} | |
</pre> | |
</template> | |
<script> | |
import { reactive, watch, computed } from 'vue' | |
import { DateTime } from 'luxon' | |
export default { | |
props: { | |
modelValue: { | |
type: Object | |
}, | |
serialize: { | |
type: Function | |
}, | |
deserialize: { | |
type: Function | |
} | |
}, | |
setup(props, { emit }) { | |
const date = computed(() => { | |
return props.deserialize(props.modelValue) | |
}) | |
const update = ($event, field) => { | |
const { year, month, day } = props.deserialize(props.modelValue) | |
let newValue | |
if (field === 'year') { | |
newValue = { year: $event.target.value, month, day } | |
} | |
if (field === 'month') { | |
newValue = { year, month: $event.target.value, day } | |
} | |
if (field === 'day') { | |
newValue = { year, month, day: $event.target.value } | |
} | |
const asObject = props.serialize(newValue) | |
if (!asObject) { | |
return | |
} | |
emit('update:modelValue', asObject) | |
} | |
return { | |
update, | |
date | |
} | |
} | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment