-
Notifications
You must be signed in to change notification settings - Fork 159
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
461f8f4
commit 81397ef
Showing
6 changed files
with
94 additions
and
218 deletions.
There are no files selected for viewing
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
18 changes: 4 additions & 14 deletions
18
packages/design-system/src/components/OcDatepicker/OcDatepicker.spec.ts
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,13 @@ | ||
import { defineComponent } from 'vue' | ||
import Datepicker from './OcDatepicker.vue' | ||
import { mount } from 'web-test-helpers' | ||
|
||
const DatePickerComponent = defineComponent({ | ||
template: '<div id="foo"><slot></slot></div>' | ||
}) | ||
|
||
describe('OcDatePicker', () => { | ||
it('renders default scoped slot', () => { | ||
const slotDefault = "<button id='default-slot'>Open datepicker</button>" | ||
it('renders', () => { | ||
const wrapper = mount(Datepicker, { | ||
slots: { default: slotDefault }, | ||
props: { value: null }, | ||
global: { | ||
renderStubDefaultSlot: true, | ||
stubs: { DatePicker: DatePickerComponent } | ||
props: { | ||
label: 'Datepicker' | ||
} | ||
}) | ||
|
||
expect(wrapper.find('#default-slot').exists()).toBeTruthy() | ||
expect(wrapper.html()).toMatchSnapshot() | ||
}) | ||
}) |
183 changes: 71 additions & 112 deletions
183
packages/design-system/src/components/OcDatepicker/OcDatepicker.vue
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,145 +1,104 @@ | ||
<template> | ||
<date-picker ref="datePicker" class="oc-datepicker" v-bind="$attrs" :popover="popperOpts"> | ||
<template #default="args"> | ||
<!-- @slot Default slot to use as the popover anchor for datepicker --> | ||
<!-- args is undefined during initial render, hence we check it here --> | ||
<slot | ||
v-if="args" | ||
:input-value="args.inputValue" | ||
:toggle-popover="args.togglePopover" | ||
:hide-popover="args.hidePopover" | ||
/> | ||
</template> | ||
</date-picker> | ||
<oc-text-input | ||
v-model="dateInputString" | ||
:label="label" | ||
type="date" | ||
:min="minDate?.toISODate()" | ||
:fix-message-line="true" | ||
:error-message="errorMessage" | ||
:clear-button-enabled="true" | ||
:clear-button-accessible-label="$gettext('Clear date')" | ||
/> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import { | ||
computed, | ||
ComponentPublicInstance, | ||
defineComponent, | ||
defineAsyncComponent, | ||
ref, | ||
unref, | ||
watch | ||
} from 'vue' | ||
import { Modifier } from '@popperjs/core' | ||
import 'v-calendar/dist/style.css' | ||
import OcSpinner from '../OcSpinner/OcSpinner.vue' | ||
import { computed, defineComponent, onMounted, PropType, ref, unref, watch } from 'vue' | ||
import { useGettext } from 'vue3-gettext' | ||
import { DateTime } from 'luxon' | ||
/** | ||
* Datepicker component based on [v-calendar](https://vcalendar.io/). For detailed documentation, please visit https://vcalendar.io/vue-3.html | ||
*/ | ||
export default defineComponent({ | ||
name: 'OcDatepicker', | ||
status: 'ready', | ||
release: '1.0.0', | ||
props: { | ||
label: { type: String, required: true }, | ||
currentDate: { type: Object as PropType<DateTime>, required: false, default: null }, | ||
minDate: { type: Object as PropType<DateTime>, required: false, default: null } | ||
}, | ||
emits: ['dateChanged'], | ||
setup(props, { emit }) { | ||
const { $gettext, current } = useGettext() | ||
const dateInputString = ref<string>('') | ||
components: { | ||
DatePicker: defineAsyncComponent({ | ||
loader: async () => { | ||
const { DatePicker } = await import('v-calendar') | ||
return DatePicker | ||
}, | ||
loadingComponent: OcSpinner | ||
const date = computed(() => { | ||
const date = DateTime.fromISO(unref(dateInputString)).endOf('day') | ||
return date.isValid ? date : null | ||
}) | ||
}, | ||
inheritAttrs: true, | ||
setup() { | ||
const datePicker = ref<ComponentPublicInstance>() | ||
const minDateUndercut = computed(() => { | ||
if (!props.minDate || !unref(date)) { | ||
return false | ||
} | ||
return unref(date) < props.minDate | ||
}) | ||
const popperOpts = computed(() => { | ||
return { | ||
modifiers: [ | ||
{ | ||
name: 'fixVerticalPosition', | ||
enabled: true, | ||
phase: 'beforeWrite', | ||
requiresIfExists: ['offset', 'flip'], | ||
fn({ state }) { | ||
const dropHeight = | ||
state.modifiersData.fullHeight || state.elements.popper.offsetHeight | ||
const rect = state.elements.popper.getBoundingClientRect() | ||
const neededScreenSpace = | ||
(state.elements.reference as HTMLElement).offsetHeight + rect.top + dropHeight | ||
const errorMessage = computed(() => { | ||
if (unref(minDateUndercut)) { | ||
return $gettext('The date must be after %{date}', { | ||
date: props.minDate | ||
.minus({ day: 1 }) | ||
.setLocale(current) | ||
.toLocaleString(DateTime.DATE_SHORT) | ||
}) | ||
} | ||
return '' | ||
}) | ||
if (state.placement !== 'top-start' && neededScreenSpace > window.innerHeight) { | ||
state.styles.popper.top = `-${150}px` | ||
} | ||
} | ||
} as Modifier<'fixVerticalPosition', unknown> | ||
] | ||
onMounted(() => { | ||
if (props.currentDate) { | ||
dateInputString.value = props.currentDate.toISODate() | ||
} | ||
}) | ||
watch( | ||
datePicker, | ||
date, | ||
() => { | ||
if (unref(datePicker)) { | ||
// for e2e tests | ||
unref(datePicker).$el.__datePicker = unref(datePicker) | ||
} | ||
emit('dateChanged', { date: unref(date), error: unref(minDateUndercut) }) | ||
}, | ||
{ immediate: true } | ||
{ | ||
deep: true | ||
} | ||
) | ||
return { popperOpts, datePicker } | ||
return { | ||
dateInputString, | ||
errorMessage | ||
} | ||
} | ||
}) | ||
</script> | ||
|
||
<style lang="scss"> | ||
.vc-pane-layout { | ||
color: var(--oc-color-text-default) !important; | ||
background-color: var(--oc-color-background-default) !important; | ||
} | ||
.vc-arrow svg path { | ||
fill: var(--oc-color-text-default) !important; | ||
} | ||
.vc-title { | ||
color: var(--oc-color-text-default) !important; | ||
} | ||
.vc-weekday { | ||
color: var(--oc-color-text-muted) !important; | ||
} | ||
.vc-day { | ||
color: var(--oc-color-text-default) !important; | ||
font-weight: var(--oc-font-weight-bold); | ||
} | ||
.vc-highlights { | ||
.vc-highlight { | ||
background-color: var(--oc-color-swatch-primary-default) !important; | ||
} | ||
+ span { | ||
color: var(--oc-color-text-inverse) !important; | ||
} | ||
} | ||
.vc-day-content.is-disabled { | ||
color: var(--oc-color-text-muted) !important; | ||
background: none; | ||
cursor: not-allowed; | ||
font-weight: var(--oc-font-weight-extralight); | ||
} | ||
</style> | ||
<docs> | ||
```js | ||
<template> | ||
<div> | ||
<oc-datepicker v-model="date"> | ||
<template #default="{ togglePopover }"> | ||
<oc-button @click="togglePopover">Open datepicker</oc-button> | ||
</template> | ||
</oc-datepicker> | ||
<p v-if="date" v-text="date" /> | ||
</div> | ||
<div> | ||
<oc-datepicker :current-date="currentDate" :min-date="minDate" label="Enter or pick a date" | ||
@date-changed="onDateChanged"/> | ||
<p v-if="selectedDate" v-text="selectedDate"/> | ||
</div> | ||
</template> | ||
<script> | ||
export default { | ||
data: () => ({ date: null }) | ||
} | ||
import {DateTime} from "luxon"; | ||
|
||
export default { | ||
data: () => ({ | ||
minDate: DateTime.now(), currentDate: DateTime.now(), selectedDate: '' | ||
}), | ||
methods: { | ||
onDateChanged({date}) { | ||
this.selectedDate = date ? date.toLocaleString(DateTime.DATE_FULL) : '' | ||
} | ||
} | ||
} | ||
</script> | ||
``` | ||
</docs> |
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
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
Oops, something went wrong.