feat: Support Dark mode for the widget (#4137)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
This commit is contained in:
parent
3813b3b372
commit
caee9535f1
36 changed files with 411 additions and 113 deletions
|
@ -1,11 +1,14 @@
|
|||
<template>
|
||||
<div class="card-message chat-bubble agent">
|
||||
<div
|
||||
class="card-message chat-bubble agent"
|
||||
:class="$dm('bg-white', 'dark:bg-slate-700')"
|
||||
>
|
||||
<img class="media" :src="mediaUrl" />
|
||||
<div class="card-body">
|
||||
<h4 class="title">
|
||||
<h4 class="title" :class="$dm('text-black-900', 'dark:text-slate-50')">
|
||||
{{ title }}
|
||||
</h4>
|
||||
<p class="body">
|
||||
<p class="body" :class="$dm('text-black-700', 'dark:text-slate-100')">
|
||||
{{ description }}
|
||||
</p>
|
||||
<card-button
|
||||
|
@ -19,11 +22,13 @@
|
|||
|
||||
<script>
|
||||
import CardButton from 'shared/components/CardButton';
|
||||
import darkModeMixin from 'widget/mixins/darkModeMixin.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CardButton,
|
||||
},
|
||||
mixins: [darkModeMixin],
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
|
@ -52,7 +57,6 @@ export default {
|
|||
@import '~dashboard/assets/scss/mixins.scss';
|
||||
|
||||
.card-message {
|
||||
background: white;
|
||||
max-width: 220px;
|
||||
padding: $space-small;
|
||||
border-radius: $space-small;
|
||||
|
@ -63,12 +67,10 @@ export default {
|
|||
font-weight: $font-weight-medium;
|
||||
margin-top: $space-smaller;
|
||||
margin-bottom: $space-smaller;
|
||||
color: $color-heading;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.body {
|
||||
color: $color-body;
|
||||
margin-bottom: $space-smaller;
|
||||
}
|
||||
|
||||
|
@ -77,10 +79,11 @@ export default {
|
|||
width: 100%;
|
||||
object-fit: contain;
|
||||
max-height: 150px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.action-button + .action-button {
|
||||
background: white;
|
||||
background: $color-white;
|
||||
@include thin-border($color-woot);
|
||||
color: $color-woot;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
<template>
|
||||
<div class="form chat-bubble agent">
|
||||
<div
|
||||
class="form chat-bubble agent"
|
||||
:class="$dm('bg-white', 'dark:bg-slate-700')"
|
||||
>
|
||||
<form @submit.prevent="onSubmit">
|
||||
<div
|
||||
v-for="item in items"
|
||||
|
@ -9,10 +12,13 @@
|
|||
'has-submitted': hasSubmitted,
|
||||
}"
|
||||
>
|
||||
<label>{{ item.label }}</label>
|
||||
<label :class="$dm('text-black-900', 'dark:text-slate-50')">{{
|
||||
item.label
|
||||
}}</label>
|
||||
<input
|
||||
v-if="item.type === 'email'"
|
||||
v-model="formValues[item.name]"
|
||||
:class="inputColor"
|
||||
:type="item.type"
|
||||
:pattern="item.regex"
|
||||
:title="item.title"
|
||||
|
@ -24,6 +30,7 @@
|
|||
<input
|
||||
v-else-if="item.type === 'text'"
|
||||
v-model="formValues[item.name]"
|
||||
:class="inputColor"
|
||||
:required="item.required && 'required'"
|
||||
:pattern="item.pattern"
|
||||
:title="item.title"
|
||||
|
@ -35,6 +42,7 @@
|
|||
<textarea
|
||||
v-else-if="item.type === 'text_area'"
|
||||
v-model="formValues[item.name]"
|
||||
:class="inputColor"
|
||||
:required="item.required && 'required'"
|
||||
:title="item.title"
|
||||
:name="item.name"
|
||||
|
@ -44,6 +52,7 @@
|
|||
<select
|
||||
v-else-if="item.type === 'select'"
|
||||
v-model="formValues[item.name]"
|
||||
:class="inputColor"
|
||||
:required="item.required && 'required'"
|
||||
>
|
||||
<option
|
||||
|
@ -73,7 +82,10 @@
|
|||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import darkModeMixin from 'widget/mixins/darkModeMixin.js';
|
||||
|
||||
export default {
|
||||
mixins: [darkModeMixin],
|
||||
props: {
|
||||
buttonLabel: {
|
||||
type: String,
|
||||
|
@ -98,6 +110,10 @@ export default {
|
|||
...mapGetters({
|
||||
widgetColor: 'appConfig/getWidgetColor',
|
||||
}),
|
||||
inputColor() {
|
||||
return `${this.$dm('bg-white', 'dark:bg-slate-600')}
|
||||
${this.$dm('text-black-900', 'dark:text-slate-50')}`;
|
||||
},
|
||||
isFormValid() {
|
||||
return this.items.reduce((acc, { name }) => {
|
||||
return !!this.formValues[name] && acc;
|
||||
|
@ -186,7 +202,6 @@ export default {
|
|||
appearance: none;
|
||||
border: 1px solid $color-border;
|
||||
border-radius: $space-smaller;
|
||||
background-color: $color-white;
|
||||
font-family: inherit;
|
||||
font-size: $space-normal;
|
||||
font-weight: normal;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
props: {
|
||||
|
@ -51,7 +52,6 @@ export default {
|
|||
background: transparent;
|
||||
border-radius: $space-large;
|
||||
border: 0;
|
||||
color: $color-woot;
|
||||
cursor: pointer;
|
||||
height: auto;
|
||||
line-height: 1.5;
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
<template>
|
||||
<div class="options-message chat-bubble agent">
|
||||
<div
|
||||
class="options-message chat-bubble agent"
|
||||
:class="$dm('bg-white', 'dark:bg-slate-700')"
|
||||
>
|
||||
<div class="card-body">
|
||||
<h4 class="title">
|
||||
<h4 class="title" :class="$dm('text-black-900', 'dark:text-slate-50')">
|
||||
{{ title }}
|
||||
</h4>
|
||||
<ul
|
||||
|
@ -23,11 +26,13 @@
|
|||
|
||||
<script>
|
||||
import ChatOption from 'shared/components/ChatOption';
|
||||
import darkModeMixin from 'widget/mixins/darkModeMixin.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ChatOption,
|
||||
},
|
||||
mixins: [darkModeMixin],
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
|
@ -80,7 +85,6 @@ export default {
|
|||
font-weight: $font-weight-normal;
|
||||
margin-top: $space-smaller;
|
||||
margin-bottom: $space-smaller;
|
||||
color: $color-heading;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
<template>
|
||||
<div class="customer-satisfcation" :style="{ borderColor: widgetColor }">
|
||||
<h6 class="title">
|
||||
<div
|
||||
class="customer-satisfaction"
|
||||
:class="$dm('bg-white', 'dark:bg-slate-700')"
|
||||
:style="{ borderColor: widgetColor }"
|
||||
>
|
||||
<h6 class="title" :class="$dm('text-slate-900', 'dark:text-slate-50')">
|
||||
{{ title }}
|
||||
</h6>
|
||||
<div class="ratings">
|
||||
|
@ -21,8 +25,9 @@
|
|||
<input
|
||||
v-model="feedback"
|
||||
class="form-input"
|
||||
:class="inputColor"
|
||||
:placeholder="$t('CSAT.PLACEHOLDER')"
|
||||
@keyup.enter="onSubmit"
|
||||
@keydown.enter="onSubmit"
|
||||
/>
|
||||
<button
|
||||
class="button small"
|
||||
|
@ -41,12 +46,14 @@ import { mapGetters } from 'vuex';
|
|||
import Spinner from 'shared/components/Spinner';
|
||||
import { CSAT_RATINGS } from 'shared/constants/messages';
|
||||
import FluentIcon from 'shared/components/FluentIcon/Index.vue';
|
||||
import darkModeMixin from 'widget/mixins/darkModeMixin';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Spinner,
|
||||
FluentIcon,
|
||||
},
|
||||
mixins: [darkModeMixin],
|
||||
props: {
|
||||
messageContentAttributes: {
|
||||
type: Object,
|
||||
|
@ -67,9 +74,7 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
widgetColor: 'appConfig/getWidgetColor',
|
||||
}),
|
||||
...mapGetters({ widgetColor: 'appConfig/getWidgetColor' }),
|
||||
isRatingSubmitted() {
|
||||
return this.messageContentAttributes?.csat_survey_response?.rating;
|
||||
},
|
||||
|
@ -80,6 +85,10 @@ export default {
|
|||
isButtonDisabled() {
|
||||
return !(this.selectedRating && this.feedback);
|
||||
},
|
||||
inputColor() {
|
||||
return `${this.$dm('bg-white', 'dark:bg-slate-600')}
|
||||
${this.$dm('text-black-900', 'dark:text-slate-50')}`;
|
||||
},
|
||||
title() {
|
||||
return this.isRatingSubmitted
|
||||
? this.$t('CSAT.SUBMITTED_TITLE')
|
||||
|
@ -136,10 +145,9 @@ export default {
|
|||
@import '~widget/assets/scss/variables.scss';
|
||||
@import '~widget/assets/scss/mixins.scss';
|
||||
|
||||
.customer-satisfcation {
|
||||
.customer-satisfaction {
|
||||
@include light-shadow;
|
||||
|
||||
background: $color-white;
|
||||
border-bottom-left-radius: $space-smaller;
|
||||
border-radius: $space-small;
|
||||
border-top: $space-micro solid $color-woot;
|
||||
|
@ -193,6 +201,10 @@ export default {
|
|||
border-top: 1px solid $color-border;
|
||||
padding: $space-one;
|
||||
width: 100%;
|
||||
|
||||
&::placeholder {
|
||||
color: $color-light-gray;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
<template>
|
||||
<div class="date--separator">
|
||||
<div
|
||||
class="date--separator"
|
||||
:class="$dm('text-slate-700', 'dark:text-slate-200')"
|
||||
>
|
||||
{{ formattedDate }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { formatDate } from 'shared/helpers/DateHelper';
|
||||
import darkModeMixin from 'widget/mixins/darkModeMixin.js';
|
||||
|
||||
export default {
|
||||
mixins: [darkModeMixin],
|
||||
props: {
|
||||
date: {
|
||||
type: String,
|
||||
|
@ -30,7 +36,6 @@ export default {
|
|||
|
||||
.date--separator {
|
||||
font-size: $font-size-default;
|
||||
color: $color-body;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
position: relative;
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
<script>
|
||||
const TYPING_INDICATOR_IDLE_TIME = 4000;
|
||||
|
||||
export default {
|
||||
props: {
|
||||
placeholder: {
|
||||
|
|
|
@ -1,17 +1,52 @@
|
|||
import { mount } from '@vue/test-utils';
|
||||
import DateSeparator from '../DateSeparator';
|
||||
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
||||
import Vuex from 'vuex';
|
||||
import VueI18n from 'vue-i18n';
|
||||
import darkModeMixin from 'widget/mixins/darkModeMixin.js';
|
||||
const localVue = createLocalVue();
|
||||
import i18n from 'dashboard/i18n';
|
||||
localVue.use(Vuex);
|
||||
localVue.use(VueI18n);
|
||||
|
||||
describe('DateSeparator', () => {
|
||||
test('matches snapshot', () => {
|
||||
const wrapper = mount(DateSeparator, {
|
||||
const i18nConfig = new VueI18n({
|
||||
locale: 'en',
|
||||
messages: i18n,
|
||||
});
|
||||
|
||||
describe('dateSeparator', () => {
|
||||
let store = null;
|
||||
let actions = null;
|
||||
let modules = null;
|
||||
let dateSeparator = null;
|
||||
|
||||
beforeEach(() => {
|
||||
actions = {};
|
||||
|
||||
modules = {
|
||||
auth: {
|
||||
getters: {
|
||||
'appConfig/darkMode': () => 'light',
|
||||
},
|
||||
},
|
||||
};
|
||||
store = new Vuex.Store({
|
||||
actions,
|
||||
modules,
|
||||
});
|
||||
|
||||
dateSeparator = shallowMount(DateSeparator, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
date: 'Nov 18, 2019',
|
||||
},
|
||||
mocks: {
|
||||
$t: () => {},
|
||||
},
|
||||
i18n: i18nConfig,
|
||||
mixins: [darkModeMixin],
|
||||
});
|
||||
expect(wrapper.vm).toBeTruthy();
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('date separator snapshot', () => {
|
||||
expect(dateSeparator.vm).toBeTruthy();
|
||||
expect(dateSeparator.element).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`DateSeparator matches snapshot 1`] = `
|
||||
exports[`dateSeparator date separator snapshot 1`] = `
|
||||
<div
|
||||
class="date--separator"
|
||||
class="date--separator text-slate-700"
|
||||
>
|
||||
|
||||
Nov 18, 2019
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue