feat: Replace rtlMixin to a composable (#9924)

This PR will replace the usage of `rtlMixin` to the `useUISettings` composable, and moved the method to component itself.
This commit is contained in:
Sivin Varghese
2024-08-12 15:50:21 +05:30
committed by GitHub
parent 96d60674aa
commit 452096f4b2
10 changed files with 74 additions and 95 deletions

View File

@@ -10,7 +10,6 @@ import PaymentPendingBanner from './components/app/PaymentPendingBanner.vue';
import PendingEmailVerificationBanner from './components/app/PendingEmailVerificationBanner.vue';
import vueActionCable from './helper/actionCable';
import WootSnackbarBox from './components/SnackbarContainer.vue';
import rtlMixin from 'shared/mixins/rtlMixin';
import { setColorTheme } from './helper/themeHelper';
import { isOnOnboardingView } from 'v3/helpers/RouteHelper';
import {
@@ -32,9 +31,6 @@ export default {
UpgradeBanner,
PendingEmailVerificationBanner,
},
mixins: [rtlMixin],
data() {
return {
showAddAccountModal: false,
@@ -46,6 +42,7 @@ export default {
computed: {
...mapGetters({
getAccount: 'accounts/getAccount',
isRTL: 'accounts/isRTL',
currentUser: 'getCurrentUser',
authUIFlags: 'getAuthUIFlags',
accountUIFlags: 'accounts/getUIFlags',
@@ -102,7 +99,6 @@ export default {
this.getAccount(this.currentAccountId);
const { pubsub_token: pubsubToken } = this.currentUser || {};
this.setLocale(locale);
this.updateRTLDirectionView(locale);
this.latestChatwootVersion = latestChatwootVersion;
vueActionCable.init(pubsubToken);
this.reconnectService = new ReconnectService(this.$store, router);
@@ -124,8 +120,8 @@ export default {
v-if="!authUIFlags.isFetching && !accountUIFlags.isFetchingItem"
id="app"
class="flex-grow-0 w-full h-full min-h-0 app-wrapper"
:class="{ 'app-rtl--wrapper': isRTLView }"
:dir="isRTLView ? 'rtl' : 'ltr'"
:class="{ 'app-rtl--wrapper': isRTL }"
:dir="isRTL ? 'rtl' : 'ltr'"
>
<UpdateBanner :latest-chatwoot-version="latestChatwootVersion" />
<template v-if="currentAccountId">

View File

@@ -31,7 +31,6 @@ import inboxMixin, { INBOX_FEATURES } from 'shared/mixins/inboxMixin';
import { trimContent, debounce } from '@chatwoot/utils';
import wootConstants from 'dashboard/constants/globals';
import { CONVERSATION_EVENTS } from '../../../helper/AnalyticsHelper/events';
import rtlMixin from 'shared/mixins/rtlMixin';
import fileUploadMixin from 'dashboard/mixins/fileUploadMixin';
import {
appendSignature,
@@ -65,7 +64,6 @@ export default {
mixins: [
inboxMixin,
messageFormatterMixin,
rtlMixin,
fileUploadMixin,
keyboardEventListenerMixins,
],
@@ -121,6 +119,7 @@ export default {
},
computed: {
...mapGetters({
isRTL: 'accounts/isRTL',
currentChat: 'getSelectedChat',
messageSignature: 'getMessageSignature',
currentUser: 'getCurrentUser',
@@ -307,7 +306,7 @@ export default {
if (this.isOnExpandedLayout || this.popoutReplyBox) {
return 'emoji-dialog--expanded';
}
if (this.isRTLView) {
if (this.isRTL) {
return 'emoji-dialog--rtl';
}
return '';

View File

@@ -1,4 +1,5 @@
<script>
import { mapGetters } from 'vuex';
import { VeTable } from 'vue-easytable';
import { getCountryFlag } from 'dashboard/helper/flag';
@@ -6,7 +7,6 @@ import Spinner from 'shared/components/Spinner.vue';
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
import EmptyState from 'dashboard/components/widgets/EmptyState.vue';
import { dynamicTime } from 'shared/helpers/timeHelper';
import rtlMixin from 'shared/mixins/rtlMixin';
import FluentIcon from 'shared/components/FluentIcon/DashboardIcon.vue';
export default {
@@ -15,7 +15,6 @@ export default {
Spinner,
VeTable,
},
mixins: [rtlMixin],
props: {
contacts: {
type: Array,
@@ -52,6 +51,9 @@ export default {
};
},
computed: {
...mapGetters({
isRTL: 'accounts/isRTL',
}),
tableData() {
if (this.isLoading) {
return [];
@@ -85,7 +87,7 @@ export default {
key: 'name',
title: this.$t('CONTACTS_PAGE.LIST.TABLE_HEADER.NAME'),
fixed: 'left',
align: this.isRTLView ? 'right' : 'left',
align: this.isRTL ? 'right' : 'left',
sortBy: this.sortConfig.name || '',
width: 300,
renderBodyCell: ({ row }) => (
@@ -121,7 +123,7 @@ export default {
field: 'email',
key: 'email',
title: this.$t('CONTACTS_PAGE.LIST.TABLE_HEADER.EMAIL_ADDRESS'),
align: this.isRTLView ? 'right' : 'left',
align: this.isRTL ? 'right' : 'left',
sortBy: this.sortConfig.email || '',
width: 240,
renderBodyCell: ({ row }) => {
@@ -145,27 +147,27 @@ export default {
key: 'phone_number',
sortBy: this.sortConfig.phone_number || '',
title: this.$t('CONTACTS_PAGE.LIST.TABLE_HEADER.PHONE_NUMBER'),
align: this.isRTLView ? 'right' : 'left',
align: this.isRTL ? 'right' : 'left',
},
{
field: 'company',
key: 'company',
sortBy: this.sortConfig.company_name || '',
title: this.$t('CONTACTS_PAGE.LIST.TABLE_HEADER.COMPANY'),
align: this.isRTLView ? 'right' : 'left',
align: this.isRTL ? 'right' : 'left',
},
{
field: 'city',
key: 'city',
sortBy: this.sortConfig.city || '',
title: this.$t('CONTACTS_PAGE.LIST.TABLE_HEADER.CITY'),
align: this.isRTLView ? 'right' : 'left',
align: this.isRTL ? 'right' : 'left',
},
{
field: 'country',
key: 'country',
title: this.$t('CONTACTS_PAGE.LIST.TABLE_HEADER.COUNTRY'),
align: this.isRTLView ? 'right' : 'left',
align: this.isRTL ? 'right' : 'left',
sortBy: this.sortConfig.country || '',
renderBodyCell: ({ row }) => {
if (row.country) {
@@ -182,7 +184,7 @@ export default {
field: 'profiles',
key: 'profiles',
title: this.$t('CONTACTS_PAGE.LIST.TABLE_HEADER.SOCIAL_PROFILES'),
align: this.isRTLView ? 'right' : 'left',
align: this.isRTL ? 'right' : 'left',
renderBodyCell: ({ row }) => {
const { profiles } = row;
@@ -213,14 +215,14 @@ export default {
key: 'last_activity_at',
sortBy: this.sortConfig.last_activity_at || '',
title: this.$t('CONTACTS_PAGE.LIST.TABLE_HEADER.LAST_ACTIVITY'),
align: this.isRTLView ? 'right' : 'left',
align: this.isRTL ? 'right' : 'left',
},
{
field: 'created_at',
key: 'created_at',
sortBy: this.sortConfig.created_at || '',
title: this.$t('CONTACTS_PAGE.LIST.TABLE_HEADER.CREATED_AT'),
align: this.isRTLView ? 'right' : 'left',
align: this.isRTL ? 'right' : 'left',
},
];
},

View File

@@ -1,6 +1,5 @@
<script>
import { mapGetters } from 'vuex';
import rtlMixin from 'shared/mixins/rtlMixin';
import NotificationPanelList from './NotificationPanelList.vue';
import { ACCOUNT_EVENTS } from '../../../../helper/AnalyticsHelper/events';
@@ -9,7 +8,6 @@ export default {
components: {
NotificationPanelList,
},
mixins: [rtlMixin],
data() {
return {
pageSize: 15,
@@ -21,9 +19,6 @@ export default {
records: 'notifications/getNotifications',
uiFlags: 'notifications/getUIFlags',
}),
notificationPanelFooterIconClass() {
return this.isRTLView ? '-mr-3' : '-ml-3';
},
totalUnreadNotifications() {
return this.meta.unreadCount;
},
@@ -201,7 +196,7 @@ export default {
<fluent-icon
icon="chevron-left"
size="16"
:class="notificationPanelFooterIconClass"
class="rtl:-mr-3 ltr:-ml-3"
/>
</woot-button>
<woot-button
@@ -236,7 +231,7 @@ export default {
<fluent-icon
icon="chevron-right"
size="16"
:class="notificationPanelFooterIconClass"
class="rtl:-mr-3 ltr:-ml-3"
/>
</woot-button>
</div>

View File

@@ -4,14 +4,12 @@ import UserAvatarWithName from 'dashboard/components/widgets/UserAvatarWithName.
import { CSAT_RATINGS } from 'shared/constants/messages';
import { mapGetters } from 'vuex';
import { messageStamp, dynamicTime } from 'shared/helpers/timeHelper';
import rtlMixin from 'shared/mixins/rtlMixin';
export default {
components: {
VeTable,
VePagination,
},
mixins: [rtlMixin],
props: {
pageIndex: {
type: Number,
@@ -20,6 +18,7 @@ export default {
},
computed: {
...mapGetters({
isRTL: 'accounts/isRTL',
csatResponses: 'csat/getCSATResponses',
metrics: 'csat/getMetrics',
}),
@@ -29,7 +28,7 @@ export default {
field: 'contact',
key: 'contact',
title: this.$t('CSAT_REPORTS.TABLE.HEADER.CONTACT_NAME'),
align: this.isRTLView ? 'right' : 'left',
align: this.isRTL ? 'right' : 'left',
width: 200,
renderBodyCell: ({ row }) => {
if (row.contact) {
@@ -48,7 +47,7 @@ export default {
field: 'assignedAgent',
key: 'assignedAgent',
title: this.$t('CSAT_REPORTS.TABLE.HEADER.AGENT_NAME'),
align: this.isRTLView ? 'right' : 'left',
align: this.isRTL ? 'right' : 'left',
width: 200,
renderBodyCell: ({ row }) => {
if (row.assignedAgent) {
@@ -78,14 +77,14 @@ export default {
field: 'feedbackText',
key: 'feedbackText',
title: this.$t('CSAT_REPORTS.TABLE.HEADER.FEEDBACK_TEXT'),
align: this.isRTLView ? 'right' : 'left',
align: this.isRTL ? 'right' : 'left',
width: 400,
},
{
field: 'conversationId',
key: 'conversationId',
title: '',
align: this.isRTLView ? 'right' : 'left',
align: this.isRTL ? 'right' : 'left',
width: 100,
renderBodyCell: ({ row }) => {
const routerParams = {

View File

@@ -1,8 +1,8 @@
<script>
import { mapGetters } from 'vuex';
import { VeTable, VePagination } from 'vue-easytable';
import Spinner from 'shared/components/Spinner.vue';
import EmptyState from 'dashboard/components/widgets/EmptyState.vue';
import rtlMixin from 'shared/mixins/rtlMixin';
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
export default {
@@ -13,7 +13,6 @@ export default {
VeTable,
VePagination,
},
mixins: [rtlMixin],
props: {
agents: {
type: Array,
@@ -33,6 +32,9 @@ export default {
},
},
computed: {
...mapGetters({
isRTL: 'accounts/isRTL',
}),
tableData() {
return this.agentMetrics
.filter(agentMetric => this.getAgentInformation(agentMetric.id))
@@ -57,7 +59,7 @@ export default {
'OVERVIEW_REPORTS.AGENT_CONVERSATIONS.TABLE_HEADER.AGENT'
),
fixed: 'left',
align: this.isRTLView ? 'right' : 'left',
align: this.isRTL ? 'right' : 'left',
width: 25,
renderBodyCell: ({ row }) => (
<div class="row-user-block">
@@ -82,7 +84,7 @@ export default {
title: this.$t(
'OVERVIEW_REPORTS.AGENT_CONVERSATIONS.TABLE_HEADER.OPEN'
),
align: this.isRTLView ? 'right' : 'left',
align: this.isRTL ? 'right' : 'left',
width: 10,
},
{
@@ -91,7 +93,7 @@ export default {
title: this.$t(
'OVERVIEW_REPORTS.AGENT_CONVERSATIONS.TABLE_HEADER.UNATTENDED'
),
align: this.isRTLView ? 'right' : 'left',
align: this.isRTL ? 'right' : 'left',
width: 10,
},
];

View File

@@ -4,6 +4,7 @@ import AccountAPI from '../../api/account';
import { differenceInDays } from 'date-fns';
import EnterpriseAccountAPI from '../../api/enterprise/account';
import { throwErrorMessage } from '../utils/api';
import { getLanguageDirection } from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
const findRecordById = ($state, id) =>
$state.records.find(record => record.id === Number(id)) || {};
@@ -27,6 +28,13 @@ export const getters = {
getUIFlags($state) {
return $state.uiFlags;
},
isRTL: ($state, _, rootState) => {
const accountId = rootState.route?.params?.accountId;
if (!accountId) return false;
const { locale } = findRecordById($state, Number(accountId));
return locale ? getLanguageDirection(locale) : false;
},
isTrialAccount: $state => id => {
const account = findRecordById($state, id);
const createdAt = new Date(account.created_at);

View File

@@ -1,4 +1,5 @@
import { getters } from '../../accounts';
import * as languageHelpers from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
const accountData = {
id: 1,
@@ -46,4 +47,37 @@ describe('#getters', () => {
)(1, 'auto_resolve_conversations')
).toEqual(true);
});
describe('isRTL', () => {
it('returns false when accountId is not present', () => {
const rootState = { route: { params: {} } };
expect(getters.isRTL({}, null, rootState)).toBe(false);
});
it('returns true for RTL language', () => {
const state = {
records: [{ id: 1, locale: 'ar' }],
};
const rootState = { route: { params: { accountId: '1' } } };
vi.spyOn(languageHelpers, 'getLanguageDirection').mockReturnValue(true);
expect(getters.isRTL(state, null, rootState)).toBe(true);
});
it('returns false for LTR language', () => {
const state = {
records: [{ id: 1, locale: 'en' }],
};
const rootState = { route: { params: { accountId: '1' } } };
vi.spyOn(languageHelpers, 'getLanguageDirection').mockReturnValue(false);
expect(getters.isRTL(state, null, rootState)).toBe(false);
});
it('returns false when account is not found', () => {
const state = {
records: [],
};
const rootState = { route: { params: { accountId: '1' } } };
expect(getters.isRTL(state, null, rootState)).toBe(false);
});
});
});

View File

@@ -1,27 +0,0 @@
import { getLanguageDirection } from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
import { useUISettings } from 'dashboard/composables/useUISettings';
export default {
setup() {
const { uiSettings, updateUISettings } = useUISettings();
return {
uiSettings,
updateUISettings,
};
},
computed: {
isRTLView() {
const { rtl_view: isRTLView } = this.uiSettings;
return isRTLView;
},
},
methods: {
updateRTLDirectionView(locale) {
const isRTLSupported = getLanguageDirection(locale);
this.updateUISettings({
rtl_view: isRTLSupported,
});
},
},
};

View File

@@ -1,29 +0,0 @@
import { shallowMount } from '@vue/test-utils';
import rtlMixin from 'shared/mixins/rtlMixin';
import { useUISettings } from 'dashboard/composables/useUISettings';
vi.mock('dashboard/composables/useUISettings');
describe('rtlMixin', () => {
const createComponent = rtl_view => {
useUISettings.mockReturnValue({
uiSettings: { rtl_view },
updateUISettings: vi.fn(),
});
return shallowMount({
render() {},
mixins: [rtlMixin],
});
};
it('returns is direction right-to-left view', () => {
const wrapper = createComponent(true);
expect(wrapper.vm.isRTLView).toBe(true);
});
it('returns is direction left-to-right view', () => {
const wrapper = createComponent(false);
expect(wrapper.vm.isRTLView).toBe(false);
});
});