feat: Support Dark mode for the widget (#4137)

Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
This commit is contained in:
Sivin Varghese 2022-04-01 20:59:03 +05:30 committed by GitHub
parent 3813b3b372
commit caee9535f1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 411 additions and 113 deletions

View file

@ -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;
}

View file

@ -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;

View file

@ -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;

View file

@ -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;
}

View file

@ -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 {

View file

@ -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;

View file

@ -12,7 +12,6 @@
<script>
const TYPING_INDICATOR_IDLE_TIME = 4000;
export default {
props: {
placeholder: {

View file

@ -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();
});
});

View file

@ -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