chore: RTL configuration (#6521)

* chore: RTL configuration

* Adds scss file
This commit is contained in:
Sivin Varghese
2023-02-23 17:50:44 +05:30
committed by GitHub
parent 87aabfbb9a
commit 409466bbd5
8 changed files with 511 additions and 3 deletions

View File

@@ -1,5 +1,10 @@
<template>
<div v-if="!authUIFlags.isFetching" id="app" class="app-wrapper app-root">
<div
v-if="!authUIFlags.isFetching"
id="app"
class="app-wrapper app-root"
:class="{ 'app-rtl--wrapper': isRTLView }"
>
<update-banner :latest-chatwoot-version="latestChatwootVersion" />
<transition name="fade" mode="out-in">
<router-view />
@@ -22,6 +27,7 @@ import NetworkNotification from './components/NetworkNotification';
import UpdateBanner from './components/app/UpdateBanner.vue';
import vueActionCable from './helper/actionCable';
import WootSnackbarBox from './components/SnackbarContainer';
import rtlMixin from 'shared/mixins/rtlMixin';
import {
registerSubscription,
verifyServiceWorkerExistence,
@@ -38,6 +44,8 @@ export default {
WootSnackbarBox,
},
mixins: [rtlMixin],
data() {
return {
showAddAccountModal: false,

View File

@@ -0,0 +1,436 @@
.app-rtl--wrapper {
direction: rtl;
// Primary sidebar
.primary--sidebar {
border-left: 1px solid var(--s-50);
border-right: 0;
.options-menu.dropdown-pane {
right: var(--space-smaller);
.auto-offline--toggle {
padding: var(--space-smaller) var(--space-one) var(--space-smaller)
var(--space-smaller);
}
}
}
// Secondary sidebar
.secondary-sidebar {
.secondary-menu {
border-left: 1px solid var(--s-50);
border-right: 0;
.nested.vertical.menu {
align-items: flex-start;
li {
direction: initial;
}
}
.secondary-menu--icon {
margin-left: var(--space-smaller);
margin-right: unset;
}
.account-context--group .account-context--switch-group {
--overlay-shadow: linear-gradient(
to left,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 1) 50%
);
background-image: var(--overlay-shadow);
}
// Help center sidebar
.sidebar-header--wrap .header-title--wrap {
margin-left: unset;
margin-right: var(--space-small);
}
}
}
// Woot button
.button {
.icon--emoji + .button__content {
padding-left: 0;
padding-right: var(--space-small);
}
.icon--font + .button__content {
padding-left: 0;
padding-right: var(--space-small);
}
.icon + .button__content {
padding-left: 0;
padding-right: var(--space-small);
}
}
// Settings header
.settings-header {
.header--icon {
margin-left: 0;
margin-right: $space-small;
}
}
.settings.back-button {
direction: initial;
margin-left: var(--space-normal);
margin-right: var(--space-smaller);
}
// Settings header action button
.button--fixed-top {
left: $space-small;
position: fixed;
right: unset;
top: $space-small;
}
// Woot Tabs
.tabs-title {
&:first-child {
margin-left: $space-slab;
margin-right: unset;
}
&:last-child {
margin-left: unset;
margin-right: $space-slab;
}
}
// woot tables
table,
thead,
th {
text-align: right;
}
// Table footer
.footer {
.page-meta {
direction: initial;
}
}
// Wizard box
.wizard-box {
direction: initial;
}
// Conversation details
.conversation-details-wrap {
.conv-header {
.user {
margin-left: var(--space-normal);
margin-right: unset;
.user--profile__meta {
margin-left: unset;
margin-right: $space-slab;
}
}
.actions--container .resolve-actions {
margin-left: unset;
margin-right: var(--space-small);
}
}
.conversation-panel {
// Message text
.text-content {
p {
unicode-bidi: plaintext;
}
ul {
padding-left: unset;
padding-right: var(--space-two);
}
li {
text-align: right;
}
}
// Message items and actions
li {
&.right {
.sender--info {
padding: var(--space-small) var(--space-smaller)
var(--space-smaller) 0;
}
.context-menu-wrap {
margin-left: 0;
margin-right: auto;
}
}
}
}
// Conversation footer
.conversation-footer {
.preview-item {
direction: initial;
}
}
// Custom attributes section in conversation sidebar
.conversation-sidebar-wrap .checkbox-wrap {
.checkbox {
margin-left: var(--space-small);
}
}
// Conversation sidebar toggle button
.sidebar-toggle--button {
transform: rotate(180deg);
}
// Conversation sidebar close button
.close-button--rtl {
transform: rotate(180deg);
}
// Resolve actions button
.resolve-actions {
.button-group .button:first-child {
border-bottom-left-radius: 0;
border-bottom-right-radius: var(--border-radius-normal);
border-top-left-radius: 0;
border-top-right-radius: var(--border-radius-normal);
}
.button-group .button:last-child {
border-bottom-left-radius: var(--border-radius-normal);
border-bottom-right-radius: 0;
border-top-left-radius: var(--border-radius-normal);
border-top-right-radius: 0;
}
}
}
// Conversation list
.conversations-list-wrap {
border-right: 0;
.conversation {
.conversation--meta {
left: $space-normal;
right: unset;
.unread {
margin-left: unset;
margin-right: auto;
}
}
.show-more--button {
transform: rotate(180deg);
}
}
.search-header--wrap .search--input {
text-align: right;
}
// Secondary sidebar toggle button
.toggle-sidebar {
margin-left: 0;
margin-right: var(--space-minus-small);
transform: rotate(180deg);
}
// Bulk actions
.bulk-action__container {
.triangle {
left: var(--triangle-position);
right: unset;
}
.bulk-action__agents {
left: var(--space-small);
right: unset;
}
.labels-container {
left: var(--space-small);
right: unset;
.label-checkbox {
margin: 0 0 0 var(--space-one);
}
}
.actions-container {
left: var(--space-small);
right: unset;
}
.bulk-action__teams {
left: var(--space-small);
right: unset;
}
}
}
// Contact notes
.card.note-wrap {
.time-stamp {
unicode-bidi: plaintext;
}
}
// Labels
.label {
direction: initial;
}
// Notification panel
.notification-wrap {
left: 0;
right: var(--space-jumbo);
.action-button {
margin-left: var(--space-small);
margin-right: 0;
}
.notification-content--wrap {
margin-left: 0;
margin-right: var(--space-slab);
}
}
// Help center
.article-container .row--article-block {
td:last-child {
direction: initial;
}
}
.header-left-wrap .page-title {
margin-right: var(--space-slab) !important;
}
.portal-container .container {
margin-left: unset !important;
margin-right: var(--space-small);
.configuration-items--wrap {
margin-left: var(--space-mega);
margin-right: unset !important;
}
thead th {
padding-left: var(--space-one);
padding-right: 0;
}
tbody td {
padding-left: var(--space-one);
padding-right: 0;
}
}
.portal-popover__container .portal {
.actions-container {
margin-left: unset;
margin-right: var(--space-one);
}
}
.edit-article--container {
.header-right--wrap {
.button-group .button:first-child {
border-bottom-left-radius: 0;
border-bottom-right-radius: var(--border-radius-normal);
border-top-left-radius: 0;
border-top-right-radius: var(--border-radius-normal);
}
.button-group .button:last-child {
border-bottom-left-radius: var(--border-radius-normal);
border-bottom-right-radius: 0;
border-top-left-radius: var(--border-radius-normal);
border-top-right-radius: 0;
}
}
.header-left--wrap {
.back-button {
direction: initial;
}
}
.article--buttons {
.dropdown-pane {
left: 0;
position: absolute;
right: unset;
}
}
.sidebar-button {
transform: rotate(180deg);
}
}
.article-settings--container {
border-left: 0;
border-right: 1px solid var(--color-border-light);
flex-direction: row-reverse;
margin-left: 0;
margin-right: var(--space-normal);
padding-left: 0;
padding-right: var(--space-normal);
}
.category-list--container .header-left--wrap {
direction: initial;
justify-content: flex-end;
}
// Widget builder
.widget-builder-container .widget-preview {
direction: initial;
}
// Other changes
.account-selector--wrap {
direction: initial;
}
.inbox--name {
direction: initial;
}
.colorpicker--chrome {
direction: initial;
}
.mention--box {
direction: initial;
}
.contact--details .contact--bio {
direction: ltr;
}
.merge-contacts .child-contact-wrap {
direction: ltr;
}
.contact--form .input-group {
direction: initial;
}
}

View File

@@ -41,6 +41,7 @@
@import 'layout';
@import 'animations';
@import 'foundation-custom';
@import 'rtl';
@import 'widgets/buttons';
@import 'widgets/conv-header';

View File

@@ -739,4 +739,9 @@ export const getLanguageName = (languageCode = '') => {
return languageObj.name || '';
};
export const getLanguageDirection = (languageCode = '') => {
const rtlLanguageIds = ['ar', 'as', 'fa', 'he', 'ku', 'ur'];
return rtlLanguageIds.includes(languageCode);
};
export default languages;

View File

@@ -1,4 +1,4 @@
import { getLanguageName } from '../languages';
import { getLanguageName, getLanguageDirection } from '../languages';
describe('#getLanguageName', () => {
it('Returns correct language name', () => {
@@ -8,3 +8,10 @@ describe('#getLanguageName', () => {
expect(getLanguageName('')).toEqual('');
});
});
describe('#getLanguageDirection', () => {
it('Returns correct language direction', () => {
expect(getLanguageDirection('es')).toEqual(false);
expect(getLanguageDirection('ar')).toEqual(true);
});
});

View File

@@ -125,9 +125,11 @@ import configMixin from 'shared/mixins/configMixin';
import accountMixin from '../../../../mixins/account';
import { FEATURE_FLAGS } from '../../../../featureFlags';
const semver = require('semver');
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import { getLanguageDirection } from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
export default {
mixins: [accountMixin, alertMixin, configMixin],
mixins: [accountMixin, alertMixin, configMixin, uiSettingsMixin],
data() {
return {
id: '',
@@ -249,11 +251,20 @@ export default {
auto_resolve_duration: this.autoResolveDuration,
});
this.$root.$i18n.locale = this.locale;
this.getAccount(this.id).locale = this.locale;
this.updateDirectionView(this.locale);
this.showAlert(this.$t('GENERAL_SETTINGS.UPDATE.SUCCESS'));
} catch (error) {
this.showAlert(this.$t('GENERAL_SETTINGS.UPDATE.ERROR'));
}
},
updateDirectionView(locale) {
const isRTLSupported = getLanguageDirection(locale);
this.updateUISettings({
rtl_view: isRTLSupported,
});
},
},
};
</script>

View File

@@ -0,0 +1,11 @@
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
export default {
mixins: [uiSettingsMixin],
computed: {
isRTLView() {
const { rtl_view: isRTLView } = this.uiSettings;
return isRTLView;
},
},
};

View File

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