diff --git a/.github/workflows/cypress-firefox.yml b/.github/workflows/cypress-firefox.yml index ce32f4bd..f6a8b9f3 100644 --- a/.github/workflows/cypress-firefox.yml +++ b/.github/workflows/cypress-firefox.yml @@ -13,3 +13,9 @@ jobs: - run: npm ci - run: npm run build - run: npm run test:cypress:firefox + - name: Upload screenshots + uses: actions/upload-artifact@v4 + if: failure() + with: + name: cypress-screenshots + path: cypress/screenshots diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml index 0a82637f..34aa195e 100644 --- a/.github/workflows/cypress.yml +++ b/.github/workflows/cypress.yml @@ -12,3 +12,9 @@ jobs: cache: "npm" - run: npm ci - run: npm test + - name: Upload screenshots + uses: actions/upload-artifact@v4 + if: failure() + with: + name: cypress-screenshots + path: cypress/screenshots diff --git a/cypress/e2e/rtl.cy.ts b/cypress/e2e/rtl.cy.ts new file mode 100644 index 00000000..64da9c0b --- /dev/null +++ b/cypress/e2e/rtl.cy.ts @@ -0,0 +1,35 @@ +describe("RTL Localization", () => { + beforeEach(() => { + cy.visitWebchat(); + cy.initMockWebchat(); + cy.setRTLDocument(); + }); + it("should render toggle button on the left", () => { + cy.get(".webchat-toggle-button").should("have.css", "left", "20px"); + }); + + it("should render webchat window on the left", () => { + cy.get(".webchat-toggle-button").click(); + cy.get(".webchat").should("have.css", "left", "20px"); + }); + + it("should render close button on the left", () => { + cy.get(".webchat-toggle-button").click(); + cy.startConversation(); + cy.get(".webchat-header-close-button").then(element => { + const rect = element[0].getBoundingClientRect(); + expect(rect.top).to.be.closeTo(0, 100); + expect(rect.left).to.be.closeTo(0, 100); + }); + }); + + it("should render bot message on the right", () => { + cy.get(".webchat-toggle-button").click(); + cy.startConversation(); + cy.receiveMessage("Hello"); + cy.get(".chat-bubble").then(element => { + const rect = element[0].getBoundingClientRect(); + expect(rect.right).to.be.closeTo(0, 100); + }); + }); +}); diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 6a18b66a..9b3b39bf 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -29,6 +29,7 @@ import "cypress-real-events/support"; import { IWebchatSettings } from '../../src/common/interfaces/webchat-config'; Cypress.Commands.add('visitWebchat', () => { + // TODO find a way to silence the logs cy.visit('/webchat.test.html', { log: false }); return cy.then(() => {}); @@ -180,6 +181,16 @@ Cypress.Commands.add('renderMessage', (text: string, data: any, source: string, return cy.then(() => {}); }); +Cypress.Commands.add('setRTLDocument', () => { + cy.document().then(doc => { + doc.documentElement.setAttribute('dir', 'rtl'); + cy.wait(500); + }); + + return cy.then(() => { }); + +}); + Cypress.Commands.add('getHistory', () => { return cy.getWebchat().then(webchat => { // @ts-ignore diff --git a/cypress/support/index.d.ts b/cypress/support/index.d.ts index 28553176..d16302f3 100644 --- a/cypress/support/index.d.ts +++ b/cypress/support/index.d.ts @@ -38,6 +38,7 @@ declare namespace Cypress { receiveMessage(text?: string, data?: Object, source?: 'bot' | 'agent' | 'user'): Chainable>; receiveMessageFixture(filename: string): Chainable; + setRTLDocument(): Chainable; /** * sends a real message as if the user wrote a text and hit the "submit" button */ diff --git a/package-lock.json b/package-lock.json index b5278085..5c210ff6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "SEE LICENSE IN LICENSE", "dependencies": { "@braintree/sanitize-url": "^6.0.0", - "@cognigy/chat-components": "0.28.0", + "@cognigy/chat-components": "0.29.0", "@cognigy/socket-client": "5.0.0-beta.17", "@emotion/cache": "^10.0.29", "@emotion/react": "^11.7.1", @@ -828,9 +828,9 @@ } }, "node_modules/@cognigy/chat-components": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@cognigy/chat-components/-/chat-components-0.28.0.tgz", - "integrity": "sha512-37deebKVjfDud9Woqww73uFO+bGMyRK0C4mdM1aSy+6kOXbAJxMetu26Zg9X6w8yD14/44qUOUStq2KPcbTvYA==", + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@cognigy/chat-components/-/chat-components-0.29.0.tgz", + "integrity": "sha512-ImEV1duNy2/OZASu0XT4FY5c+GyJU2iD65+YphxnzHBPomOtzUHFu1H/nzWu3s9CEKBaf6L/tRjNHBIs8tHBfA==", "dependencies": { "@braintree/sanitize-url": "^6.0.4", "@fontsource/figtree": "5.0.19", diff --git a/package.json b/package.json index b9fa278d..e6728984 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "dev": "webpack-dev-server --config webpack.dev.js --host 0.0.0.0", "cypress:open": "cypress open --e2e --browser chrome", "cypress:serve": "http-server -a localhost -p 8787 dist/", - "cypress:run": "cypress run", + "cypress:run": "cypress run --browser chrome", "cypress:run:firefox": "cypress run --browser firefox", "test:cypress": "run-p -r cypress:serve cypress:run", "test:cypress:firefox": "run-p -r cypress:serve cypress:run:firefox", @@ -28,7 +28,7 @@ }, "dependencies": { "@braintree/sanitize-url": "^6.0.0", - "@cognigy/chat-components": "0.28.0", + "@cognigy/chat-components": "0.29.0", "@cognigy/socket-client": "5.0.0-beta.17", "@emotion/cache": "^10.0.29", "@emotion/react": "^11.7.1", diff --git a/src/webchat-embed/embedded-webchat-styles.css b/src/webchat-embed/embedded-webchat-styles.css index a1c88274..38afa340 100644 --- a/src/webchat-embed/embedded-webchat-styles.css +++ b/src/webchat-embed/embedded-webchat-styles.css @@ -25,6 +25,11 @@ z-index: 1; } +[data-cognigy-webchat-root] button[data-cognigy-webchat-toggle]:dir(rtl) { + left: 20px; + right: unset; +} + [data-cognigy-webchat-root] button[data-cognigy-webchat-toggle]:focus { box-shadow: 0 0 4px 3px rgb(0, 0, 0, 0.2); } @@ -41,6 +46,11 @@ height: 100%; } + [data-cognigy-webchat-root]:dir(rtl) { + right: auto; + left: unset; + } + [data-cognigy-webchat-root] [data-cognigy-webchat] { left: auto; top: auto; @@ -54,6 +64,12 @@ border-radius: 16px; box-shadow: 0 5px 18px 0 rgba(151, 124, 156, 0.2), 0 5px 32px 0 rgba(203, 195, 212, 0.2), 0 8px 58px 0 rgba(216, 212, 221, 0.1); } + + [data-cognigy-webchat-root] [data-cognigy-webchat]:dir(rtl) { + right: auto; + left: 20px; + } + } @media screen and (min-width: 576px) and (max-height: 870px) { @@ -61,4 +77,12 @@ height: calc(100% - 90px); min-height: 558px; } +} + +.cc-rtl-flip { + transform: scaleX(-1); +} + +.cc-rtl-flip-invert { + transform: scaleX(1) !important; } \ No newline at end of file diff --git a/src/webchat-ui/components/WebchatUI.tsx b/src/webchat-ui/components/WebchatUI.tsx index 3ba7e5dc..8c640bca 100644 --- a/src/webchat-ui/components/WebchatUI.tsx +++ b/src/webchat-ui/components/WebchatUI.tsx @@ -215,6 +215,21 @@ const RegularLayoutContentWrapper = styled.div(({ theme }) => ({ transform: "translateX(100%)", transition: "transform 400ms ease-out", }, + + "&.slide-in-enter:dir(rtl)": { + transform: "translateX(-100%)", + }, + "&.slide-in-enter-active:dir(rtl)": { + transform: "translateX(0%)", + transition: "transform 400ms ease-out", + }, + "&.slide-in-exit:dir(rtl)": { + transform: "translateX(0%)", + }, + "&.slide-in-exit-active:dir(rtl)": { + transform: "translateX(-100%)", + transition: "transform 400ms ease-out", + }, })); export class WebchatUI extends React.PureComponent< diff --git a/src/webchat-ui/components/plugins/input/base/BaseInput.tsx b/src/webchat-ui/components/plugins/input/base/BaseInput.tsx index d1eb44ee..2e7a271f 100644 --- a/src/webchat-ui/components/plugins/input/base/BaseInput.tsx +++ b/src/webchat-ui/components/plugins/input/base/BaseInput.tsx @@ -505,7 +505,7 @@ export class BaseInput extends React.PureComponent diff --git a/src/webchat-ui/components/presentational/Header.tsx b/src/webchat-ui/components/presentational/Header.tsx index c2dfa5cd..f44c490d 100644 --- a/src/webchat-ui/components/presentational/Header.tsx +++ b/src/webchat-ui/components/presentational/Header.tsx @@ -146,7 +146,7 @@ const Header: FC = props => { diff --git a/src/webchat-ui/components/presentational/previous-conversations/ConversationsListItem.tsx b/src/webchat-ui/components/presentational/previous-conversations/ConversationsListItem.tsx index e0b57eb9..21b270e6 100644 --- a/src/webchat-ui/components/presentational/previous-conversations/ConversationsListItem.tsx +++ b/src/webchat-ui/components/presentational/previous-conversations/ConversationsListItem.tsx @@ -145,7 +145,7 @@ export const ConversationsListItem = (props: IConversationsListItemProps) => { - + ); diff --git a/src/webchat-ui/style.ts b/src/webchat-ui/style.ts index 4c857c34..bb2394fd 100644 --- a/src/webchat-ui/style.ts +++ b/src/webchat-ui/style.ts @@ -120,8 +120,12 @@ const deriveDisabledColor = (color: string) => { export const createWebchatTheme = (theme: Partial = {}): IWebchatTheme => { + const htmlDirection = document?.documentElement?.dir; + const bodyDirection = document?.body?.dir; + const isRTL = htmlDirection === 'rtl' || bodyDirection === 'rtl'; + // Webchat endpoint default color - const webchatEndpointDefaultColor = '#2C6CAF'; + const webchatEndpointDefaultColor = '#2455E6'; // Webchat 3 Theme color defaults const primaryColor = '#2455E6'; @@ -129,6 +133,9 @@ export const createWebchatTheme = (theme: Partial = {}): IWebchat const secondaryColor = '#1A1A1A'; let backgroundHome = 'radial-gradient(204.5% 136.79% at 0.53% 95.79%, #EDECF9 0%, #BFBAFF 31.77%, #2152E3 65.63%, #05309E 100%)'; + if (isRTL) { + backgroundHome = 'radial-gradient(at right 95.79%, hsl(225, 80%, 32%) 0%, #2455E6 34.37%, hsl(225, 80%, 72%) 68.23%, hsl(225, 79%, 92%) 100%)'; + } const backgroundWebchat = "#FFFFFF"; const backgroundBotMessage = "#FFFFFF"; const backgroundUserMessage = "#E8EBFF"; @@ -164,7 +171,6 @@ export const createWebchatTheme = (theme: Partial = {}): IWebchat } } - if (!theme.primaryColor) theme.primaryColor = primaryColor;