mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-01 19:48:08 +00:00
feat: Vite + vue 3 💚 (#10047)
Fixes https://github.com/chatwoot/chatwoot/issues/8436 Fixes https://github.com/chatwoot/chatwoot/issues/9767 Fixes https://github.com/chatwoot/chatwoot/issues/10156 Fixes https://github.com/chatwoot/chatwoot/issues/6031 Fixes https://github.com/chatwoot/chatwoot/issues/5696 Fixes https://github.com/chatwoot/chatwoot/issues/9250 Fixes https://github.com/chatwoot/chatwoot/issues/9762 --------- Co-authored-by: Pranav <pranavrajs@gmail.com> Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
This commit is contained in:
@@ -50,7 +50,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
onClick(e) {
|
||||
this.$emit('click', e);
|
||||
this.$emit('primaryAction', e);
|
||||
},
|
||||
onClickClose(e) {
|
||||
this.$emit('close', e);
|
||||
@@ -61,7 +61,7 @@ export default {
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex items-center justify-center h-12 gap-4 px-4 py-3 text-xs text-white banner dark:text-white"
|
||||
class="flex items-center justify-center h-12 gap-4 px-4 py-3 text-xs text-white banner dark:text-white woot-banner"
|
||||
:class="bannerClasses"
|
||||
>
|
||||
<span class="banner-message">
|
||||
|
||||
@@ -1,43 +1,38 @@
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
x: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
y: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
left: this.x,
|
||||
top: this.y,
|
||||
show: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
style() {
|
||||
return {
|
||||
top: this.top + 'px',
|
||||
left: this.left + 'px',
|
||||
};
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => this.$el.focus());
|
||||
},
|
||||
};
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, nextTick, defineEmits } from 'vue';
|
||||
|
||||
const { x, y } = defineProps({
|
||||
x: { type: Number, default: 0 },
|
||||
y: { type: Number, default: 0 },
|
||||
});
|
||||
const emit = defineEmits(['close']);
|
||||
|
||||
const left = ref(x);
|
||||
const top = ref(y);
|
||||
|
||||
const style = computed(() => ({
|
||||
top: top.value + 'px',
|
||||
left: left.value + 'px',
|
||||
}));
|
||||
|
||||
const target = ref();
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
target.value.focus();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="fixed outline-none z-[9999] cursor-pointer"
|
||||
:style="style"
|
||||
tabindex="0"
|
||||
@blur="$emit('close')"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
<Teleport to="body">
|
||||
<div
|
||||
ref="target"
|
||||
class="fixed outline-none z-[9999] cursor-pointer"
|
||||
:style="style"
|
||||
tabindex="0"
|
||||
@blur="emit('close')"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import DatePicker from 'vue-datepicker-next';
|
||||
export default {
|
||||
components: { DatePicker },
|
||||
props: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import addDays from 'date-fns/addDays';
|
||||
import DatePicker from 'vue2-datepicker';
|
||||
import DatePicker from 'vue-datepicker-next';
|
||||
export default {
|
||||
components: { DatePicker },
|
||||
props: {
|
||||
|
||||
@@ -18,7 +18,6 @@ defineProps({
|
||||
<template>
|
||||
<button
|
||||
class="inline-flex relative items-center p-1.5 w-fit h-8 gap-1.5 rounded-lg hover:bg-slate-50 dark:hover:bg-slate-800 active:bg-slate-75 dark:active:bg-slate-800"
|
||||
@click="$emit('click')"
|
||||
>
|
||||
<slot name="leftIcon">
|
||||
<fluent-icon
|
||||
|
||||
@@ -38,15 +38,19 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['onSearch']);
|
||||
const emit = defineEmits(['onSearch', 'select']);
|
||||
|
||||
const searchTerm = ref('');
|
||||
|
||||
const onSearch = debounce(value => {
|
||||
searchTerm.value = value;
|
||||
const debouncedEmit = debounce(value => {
|
||||
emit('onSearch', value);
|
||||
}, 300);
|
||||
|
||||
const onSearch = value => {
|
||||
searchTerm.value = value;
|
||||
debouncedEmit();
|
||||
};
|
||||
|
||||
const filteredListItems = computed(() => {
|
||||
if (!searchTerm.value) return props.listItems;
|
||||
return picoSearch(props.listItems, searchTerm.value, ['name']);
|
||||
@@ -84,7 +88,7 @@ const shouldShowEmptyState = computed(() => {
|
||||
:input-placeholder="inputPlaceholder"
|
||||
:show-clear-filter="showClearFilter"
|
||||
@input="onSearch"
|
||||
@click="$emit('removeFilter')"
|
||||
@remove="$emit('removeFilter')"
|
||||
/>
|
||||
</slot>
|
||||
<slot name="listItem">
|
||||
@@ -103,7 +107,7 @@ const shouldShowEmptyState = computed(() => {
|
||||
:button-text="item.name"
|
||||
:icon="item.icon"
|
||||
:icon-color="item.iconColor"
|
||||
@click="$emit('click', item)"
|
||||
@click.stop.prevent="emit('select', item)"
|
||||
/>
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
@@ -22,10 +22,6 @@ defineProps({
|
||||
<template>
|
||||
<button
|
||||
class="relative inline-flex items-center justify-start w-full p-3 border-0 rounded-none first:rounded-t-xl last:rounded-b-xl h-11 hover:bg-slate-50 dark:hover:bg-slate-700 active:bg-slate-75 dark:active:bg-slate-800"
|
||||
@click.stop.prevent="$emit('click')"
|
||||
@mouseenter="$emit('mouseenter')"
|
||||
@mouseleave="$emit('mouseleave')"
|
||||
@focus="$emit('focus')"
|
||||
>
|
||||
<div class="inline-flex items-center gap-3 overflow-hidden">
|
||||
<fluent-icon
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup>
|
||||
import { defineEmits } from 'vue';
|
||||
defineProps({
|
||||
inputValue: {
|
||||
type: String,
|
||||
@@ -13,6 +14,8 @@ defineProps({
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['input', 'remove']);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -30,7 +33,7 @@ defineProps({
|
||||
class="w-full mb-0 text-sm bg-white dark:bg-slate-800 text-slate-800 dark:text-slate-75 reset-base"
|
||||
:placeholder="inputPlaceholder"
|
||||
:value="inputValue"
|
||||
@input="$emit('input', $event.target.value)"
|
||||
@input="emit('input', $event.target.value)"
|
||||
/>
|
||||
</div>
|
||||
<!-- Clear filter button -->
|
||||
@@ -40,7 +43,7 @@ defineProps({
|
||||
variant="clear"
|
||||
color-scheme="primary"
|
||||
class="!px-1 !py-1.5"
|
||||
@click="$emit('click')"
|
||||
@click="emit('remove')"
|
||||
>
|
||||
{{ $t('REPORT.FILTER_ACTIONS.CLEAR_FILTER') }}
|
||||
</woot-button>
|
||||
|
||||
@@ -74,7 +74,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
onClick() {
|
||||
this.$emit('click', this.title);
|
||||
this.$emit('remove', this.title);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: { type: Boolean, default: false },
|
||||
modelValue: { type: Boolean, default: false },
|
||||
size: { type: String, default: '' },
|
||||
},
|
||||
methods: {
|
||||
onClick() {
|
||||
this.$emit('input', !this.value);
|
||||
this.$emit('update:modelValue', !this.modelValue);
|
||||
this.$emit('input', !this.modelValue);
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -16,12 +17,12 @@ export default {
|
||||
<button
|
||||
type="button"
|
||||
class="toggle-button p-0"
|
||||
:class="{ active: value, small: size === 'small' }"
|
||||
:class="{ active: modelValue, small: size === 'small' }"
|
||||
role="switch"
|
||||
:aria-checked="value.toString()"
|
||||
:aria-checked="modelValue.toString()"
|
||||
@click="onClick"
|
||||
>
|
||||
<span aria-hidden="true" :class="{ active: value }" />
|
||||
<span aria-hidden="true" :class="{ active: modelValue }" />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
export default {
|
||||
name: 'WootTabs',
|
||||
props: {
|
||||
index: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return { hasScroll: false };
|
||||
},
|
||||
created() {
|
||||
window.addEventListener('resize', this.computeScrollWidth);
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.computeScrollWidth);
|
||||
},
|
||||
mounted() {
|
||||
this.computeScrollWidth();
|
||||
},
|
||||
methods: {
|
||||
computeScrollWidth() {
|
||||
const tabElement = this.$el.getElementsByClassName('tabs')[0];
|
||||
this.hasScroll = tabElement.scrollWidth > tabElement.clientWidth;
|
||||
},
|
||||
onScrollClick(direction) {
|
||||
const tabElement = this.$el.getElementsByClassName('tabs')[0];
|
||||
let scrollPosition = tabElement.scrollLeft;
|
||||
if (direction === 'left') {
|
||||
scrollPosition -= 100;
|
||||
} else {
|
||||
scrollPosition += 100;
|
||||
}
|
||||
tabElement.scrollTo({
|
||||
top: 0,
|
||||
left: scrollPosition,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
},
|
||||
createScrollButton(createElement, direction) {
|
||||
if (!this.hasScroll) {
|
||||
return false;
|
||||
}
|
||||
return createElement(
|
||||
'button',
|
||||
{
|
||||
class: 'tabs--scroll-button button clear secondary button--only-icon',
|
||||
on: { click: () => this.onScrollClick(direction) },
|
||||
},
|
||||
[
|
||||
createElement('fluent-icon', {
|
||||
props: { icon: `chevron-${direction}`, size: 16 },
|
||||
}),
|
||||
]
|
||||
);
|
||||
},
|
||||
},
|
||||
render(createElement) {
|
||||
const Tabs = this.$slots.default
|
||||
.filter(
|
||||
node =>
|
||||
node.componentOptions &&
|
||||
node.componentOptions.tag === 'woot-tabs-item'
|
||||
)
|
||||
.map((node, index) => {
|
||||
const data = node.componentOptions.propsData;
|
||||
data.index = index;
|
||||
return node;
|
||||
});
|
||||
const leftButton = this.createScrollButton(createElement, 'left');
|
||||
const rightButton = this.createScrollButton(createElement, 'right');
|
||||
return (
|
||||
<div
|
||||
class={{
|
||||
'tabs--container--with-border': this.border,
|
||||
'tabs--container': true,
|
||||
}}
|
||||
>
|
||||
{leftButton}
|
||||
<ul class={{ tabs: true, 'tabs--with-scroll': this.hasScroll }}>
|
||||
{Tabs}
|
||||
</ul>
|
||||
{rightButton}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
||||
89
app/javascript/dashboard/components/ui/Tabs/Tabs.vue
Normal file
89
app/javascript/dashboard/components/ui/Tabs/Tabs.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<script setup>
|
||||
// [VITE] TODO: Test this component across different screen sizes and usages
|
||||
import { ref, provide, onMounted, computed } from 'vue';
|
||||
import { useEventListener } from '@vueuse/core';
|
||||
|
||||
const props = defineProps({
|
||||
index: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['change']);
|
||||
|
||||
const hasScroll = ref(false);
|
||||
// TODO: We may not this internalActiveIndex, we can use activeIndex directly
|
||||
// But right I'll keep it and fix it when testing the rest of the codebase
|
||||
const internalActiveIndex = ref(props.index);
|
||||
|
||||
// Create a proxy for activeIndex using computed
|
||||
const activeIndex = computed({
|
||||
get: () => internalActiveIndex.value,
|
||||
set: newValue => {
|
||||
internalActiveIndex.value = newValue;
|
||||
emit('change', newValue);
|
||||
},
|
||||
});
|
||||
|
||||
provide('activeIndex', activeIndex);
|
||||
provide('updateActiveIndex', index => {
|
||||
activeIndex.value = index;
|
||||
});
|
||||
|
||||
const computeScrollWidth = () => {
|
||||
// TODO: use useElementSize from vueuse
|
||||
const tabElement = document.querySelector('.tabs');
|
||||
if (tabElement) {
|
||||
hasScroll.value = tabElement.scrollWidth > tabElement.clientWidth;
|
||||
}
|
||||
};
|
||||
|
||||
const onScrollClick = direction => {
|
||||
// TODO: use useElementSize from vueuse
|
||||
const tabElement = document.querySelector('.tabs');
|
||||
if (tabElement) {
|
||||
let scrollPosition = tabElement.scrollLeft;
|
||||
scrollPosition += direction === 'left' ? -100 : 100;
|
||||
tabElement.scrollTo({
|
||||
top: 0,
|
||||
left: scrollPosition,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEventListener(window, 'resize', computeScrollWidth);
|
||||
onMounted(() => {
|
||||
computeScrollWidth();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="{ 'tabs--container--with-border': border }"
|
||||
class="tabs--container"
|
||||
>
|
||||
<button
|
||||
v-if="hasScroll"
|
||||
class="tabs--scroll-button button clear secondary button--only-icon"
|
||||
@click="onScrollClick('left')"
|
||||
>
|
||||
<fluent-icon icon="chevron-left" :size="16" />
|
||||
</button>
|
||||
<ul :class="{ 'tabs--with-scroll': hasScroll }" class="tabs">
|
||||
<slot />
|
||||
</ul>
|
||||
<button
|
||||
v-if="hasScroll"
|
||||
class="tabs--scroll-button button clear secondary button--only-icon"
|
||||
@click="onScrollClick('right')"
|
||||
>
|
||||
<fluent-icon icon="chevron-right" :size="16" />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,47 +1,40 @@
|
||||
<script>
|
||||
export default {
|
||||
name: 'WootTabsItem',
|
||||
props: {
|
||||
index: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
count: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
showBadge: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
<script setup>
|
||||
import { computed, inject } from 'vue';
|
||||
|
||||
computed: {
|
||||
active() {
|
||||
return this.index === this.$parent.index;
|
||||
},
|
||||
|
||||
getItemCount() {
|
||||
return this.count;
|
||||
},
|
||||
const props = defineProps({
|
||||
index: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
|
||||
methods: {
|
||||
onTabClick(event) {
|
||||
event.preventDefault();
|
||||
if (!this.disabled) {
|
||||
this.$parent.$emit('change', this.index);
|
||||
}
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
count: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
showBadge: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
|
||||
const activeIndex = inject('activeIndex');
|
||||
const updateActiveIndex = inject('updateActiveIndex');
|
||||
|
||||
const active = computed(() => props.index === activeIndex.value);
|
||||
const getItemCount = computed(() => props.count);
|
||||
|
||||
const onTabClick = event => {
|
||||
event.preventDefault();
|
||||
if (!props.disabled) {
|
||||
updateActiveIndex(props.index);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ export default {
|
||||
this.createTimer();
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
unmounted() {
|
||||
clearTimeout(this.timer);
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -96,11 +96,6 @@ export default {
|
||||
);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleClick(evt) {
|
||||
this.$emit('click', evt);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -110,7 +105,6 @@ export default {
|
||||
:type="type"
|
||||
:class="buttonClasses"
|
||||
:disabled="isDisabled || isLoading"
|
||||
@click="handleClick"
|
||||
>
|
||||
<Spinner
|
||||
v-if="isLoading"
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import WootAnnouncementPopup from '../AnnouncementPopup.vue';
|
||||
|
||||
export default {
|
||||
title: 'Components/Popup/Announcement Popup',
|
||||
argTypes: {
|
||||
popupMessage: {
|
||||
defaultValue:
|
||||
'Now a new key shortcut (⌘ + ↵) is available to send messages. You can enable it in the',
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
routeText: {
|
||||
defaultValue: 'profile settings',
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
hasCloseButton: {
|
||||
defaultValue: true,
|
||||
control: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
closeButtonText: {
|
||||
defaultValue: 'Got it',
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args, { argTypes }) => ({
|
||||
props: Object.keys(argTypes),
|
||||
components: { WootAnnouncementPopup },
|
||||
template:
|
||||
'<woot-announcement-popup v-bind="$props" @open="onClickOpenPath" @close="onClickClose"></woot-announcement-popup>',
|
||||
});
|
||||
|
||||
export const AnnouncementPopup = Template.bind({});
|
||||
AnnouncementPopup.args = {
|
||||
onClickOpenPath: action('opened path'),
|
||||
onClickClose: action('closed the popup'),
|
||||
};
|
||||
@@ -1,50 +0,0 @@
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import WootButton from '../WootButton.vue';
|
||||
|
||||
export default {
|
||||
title: 'Components/Button',
|
||||
component: WootButton,
|
||||
argTypes: {
|
||||
colorScheme: {
|
||||
control: {
|
||||
type: 'select',
|
||||
options: ['primary', 'secondary', 'success', 'alert', 'warning'],
|
||||
},
|
||||
},
|
||||
size: {
|
||||
control: {
|
||||
type: 'select',
|
||||
options: ['tiny', 'small', 'medium', 'large', 'expanded'],
|
||||
},
|
||||
},
|
||||
variant: {
|
||||
control: {
|
||||
type: 'select',
|
||||
options: ['hollow', 'clear'],
|
||||
},
|
||||
},
|
||||
isLoading: {
|
||||
control: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
isDisabled: {
|
||||
control: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args, { argTypes }) => ({
|
||||
props: Object.keys(argTypes),
|
||||
components: { WootButton },
|
||||
template:
|
||||
'<woot-button v-bind="$props" @click="onClick">{{label}}</woot-button>',
|
||||
});
|
||||
|
||||
export const Primary = Template.bind({});
|
||||
Primary.args = {
|
||||
label: 'New message',
|
||||
onClick: action('Hello'),
|
||||
};
|
||||
@@ -1,38 +0,0 @@
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import WootDateRangePicker from '../DateRangePicker.vue';
|
||||
|
||||
export default {
|
||||
title: 'Components/Date Picker/Date Range Picker',
|
||||
argTypes: {
|
||||
confirmText: {
|
||||
defaultValue: 'Apply',
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
placeholder: {
|
||||
defaultValue: 'Select date range',
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
value: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args, { argTypes }) => ({
|
||||
props: Object.keys(argTypes),
|
||||
components: { WootDateRangePicker },
|
||||
template:
|
||||
'<woot-date-range-picker v-bind="$props" @change="onChange"></woot-date-range-picker>',
|
||||
});
|
||||
|
||||
export const DateRangePicker = Template.bind({});
|
||||
DateRangePicker.args = {
|
||||
onChange: action('applied'),
|
||||
value: new Date(),
|
||||
};
|
||||
@@ -1,38 +0,0 @@
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import WootDateTimePicker from '../DateTimePicker.vue';
|
||||
|
||||
export default {
|
||||
title: 'Components/Date Picker/Date Time Picker',
|
||||
argTypes: {
|
||||
confirmText: {
|
||||
defaultValue: 'Apply',
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
placeholder: {
|
||||
defaultValue: 'Select date time',
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
value: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args, { argTypes }) => ({
|
||||
props: Object.keys(argTypes),
|
||||
components: { WootDateTimePicker },
|
||||
template:
|
||||
'<woot-date-time-picker v-bind="$props" @change="onChange"></woot-date-time-picker>',
|
||||
});
|
||||
|
||||
export const DateTimePicker = Template.bind({});
|
||||
DateTimePicker.args = {
|
||||
onChange: action('applied'),
|
||||
value: new Date(),
|
||||
};
|
||||
@@ -1,64 +0,0 @@
|
||||
import { action } from '@storybook/addon-actions';
|
||||
|
||||
export default {
|
||||
title: 'Components/Label',
|
||||
argTypes: {
|
||||
title: {
|
||||
defaultValue: 'sales',
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
colorScheme: {
|
||||
control: {
|
||||
type: 'select',
|
||||
options: ['primary', 'secondary', 'success', 'alert', 'warning'],
|
||||
},
|
||||
},
|
||||
description: {
|
||||
defaultValue: 'label',
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
href: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
bgColor: {
|
||||
defaultValue: '#a83262',
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
small: {
|
||||
defaultValue: false,
|
||||
control: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
showClose: {
|
||||
defaultValue: false,
|
||||
control: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
icon: {
|
||||
defaultValue: 'ion-close',
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args, { argTypes }) => ({
|
||||
props: Object.keys(argTypes),
|
||||
template: '<woot-label v-bind="$props" @click="onClick"></woot-label>',
|
||||
});
|
||||
|
||||
export const DefaultLabel = Template.bind({});
|
||||
DefaultLabel.args = {
|
||||
onClick: action('clicked'),
|
||||
};
|
||||
@@ -1,30 +0,0 @@
|
||||
import TimeAgo from 'dashboard/components/ui/TimeAgo';
|
||||
|
||||
export default {
|
||||
title: 'Components/TimeAgo',
|
||||
component: TimeAgo,
|
||||
argTypes: {
|
||||
isAutoRefreshEnabled: {
|
||||
control: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
timestamp: {
|
||||
control: {
|
||||
type: 'text, date, number',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args, { argTypes }) => ({
|
||||
props: Object.keys(argTypes),
|
||||
components: { TimeAgo },
|
||||
template: '<time-ago v-bind="$props"></time-ago>',
|
||||
});
|
||||
|
||||
export const TimeAgoView = Template.bind({});
|
||||
TimeAgoView.args = {
|
||||
timestamp: 1549843200,
|
||||
isAutoRefreshEnabled: false,
|
||||
};
|
||||
Reference in New Issue
Block a user