diff --git a/Makefile b/Makefile index aecbca5..f4913e2 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,7 @@ build: rm -rf ./dist ./node_modules/.bin/fedx-scripts babel src --out-dir dist --source-maps --ignore **/*.test.jsx,**/__mocks__,**/__snapshots__,**/setupTest.js --copy-files @# --copy-files will bring in everything else that wasn't processed by babel. Remove what we don't want. + @rm -rf dist/*.test.jsx @rm -rf dist/**/*.test.jsx @rm -rf dist/**/__snapshots__ @rm -rf dist/__mocks__ diff --git a/package-lock.json b/package-lock.json index c2b0eba..7f123f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@edx/paragon": "21.5.3", "@reduxjs/toolkit": "^1.9.7", "babel-polyfill": "6.26.0", + "prop-types": "15.8.1", "react": "17.0.2", "react-dom": "17.0.2", "react-redux": "7.2.9", @@ -30,12 +31,12 @@ "@testing-library/dom": "9.3.3", "@testing-library/jest-dom": "5.17.0", "@testing-library/react": "10.4.9", + "@testing-library/user-event": "^14.5.1", "@wojtekmaj/enzyme-adapter-react-17": "0.8.0", "enzyme": "3.11.0", "husky": "8.0.3", "jest": "29.7.0", - "jest-chain": "1.1.6", - "prop-types": "15.8.1" + "jest-chain": "1.1.6" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -5171,6 +5172,19 @@ "node": ">= 10" } }, + "node_modules/@testing-library/user-event": { + "version": "14.5.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.1.tgz", + "integrity": "sha512-UCcUKrUYGj7ClomOo2SpNVvx4/fkd/2BbIHDCle8A0ax+P3bU7yJwDBDrS6ZwdTMARWTGODX1hEsCcO+7beJjg==", + "dev": true, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, "node_modules/@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -25697,6 +25711,13 @@ } } }, + "@testing-library/user-event": { + "version": "14.5.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.1.tgz", + "integrity": "sha512-UCcUKrUYGj7ClomOo2SpNVvx4/fkd/2BbIHDCle8A0ax+P3bU7yJwDBDrS6ZwdTMARWTGODX1hEsCcO+7beJjg==", + "dev": true, + "requires": {} + }, "@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", diff --git a/package.json b/package.json index aba3c8e..ab00db5 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,9 @@ "start": "fedx-scripts webpack-dev-server --progress", "test": "fedx-scripts jest --coverage" }, + "files": [ + "/dist" + ], "repository": { "type": "git", "url": "git+https://github.com/openedx/frontend-component-ai-translations.git" @@ -33,18 +36,19 @@ "@testing-library/dom": "9.3.3", "@testing-library/jest-dom": "5.17.0", "@testing-library/react": "10.4.9", + "@testing-library/user-event": "^14.5.1", "@wojtekmaj/enzyme-adapter-react-17": "0.8.0", "enzyme": "3.11.0", "husky": "8.0.3", "jest": "29.7.0", - "jest-chain": "1.1.6", - "prop-types": "15.8.1" + "jest-chain": "1.1.6" }, "dependencies": { "@edx/frontend-platform": "6.0.1", "@edx/paragon": "21.5.3", "@reduxjs/toolkit": "^1.9.7", "babel-polyfill": "6.26.0", + "prop-types": "15.8.1", "react": "17.0.2", "react-dom": "17.0.2", "react-redux": "7.2.9", diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 0000000..700c5f8 --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,103 @@ +import { useState } from 'react'; +import { + ActionRow, Collapsible, Icon, IconButton, Image, +} from '@edx/paragon'; +import { ChevronLeft, ChevronRight, Close } from '@edx/paragon/icons'; +import PropTypes from 'prop-types'; + +import XpertLogo from './XpertLogo'; + +const App = ({ setIsAiTranslations, closeTranscriptSettings }) => { + const googleTranslateImage = 'https://prod-edx-ai-translations-assets.s3.amazonaws.com/google-translate.png'; + const [appState, setAppState] = useState({ + selectedLanguages: [], + translationsError: false, + view: '', + }); + + const handleAppState = (updatedData) => { + setAppState((previousState) => ({ ...previousState, ...updatedData })); + }; + + const handleViewClick = () => { + handleAppState({ view: 'request' }); + setIsAiTranslations(true); + }; + + const handleBackButton = () => { + if (appState.view === 'request') { + setIsAiTranslations(false); + setAppState({ ...appState, view: '' }); + } + }; + + return ( +
+ {!appState.view && ( + + +
+ + Get free translations + +
+ +
+
+ )} + {appState.view && ( + <> + + + + { + closeTranscriptSettings(); + setIsAiTranslations(false); + }} + src={Close} + alt="close settings" + data-testid="action-row-close-btn" + /> + +
+ Translations is not available +
+ + )} +
+ ); +}; + +App.propTypes = { + setIsAiTranslations: PropTypes.func.isRequired, + closeTranscriptSettings: PropTypes.func.isRequired, +}; + +export default App; diff --git a/src/App.test.jsx b/src/App.test.jsx new file mode 100644 index 0000000..a40d160 --- /dev/null +++ b/src/App.test.jsx @@ -0,0 +1,84 @@ +import * as React from 'react'; +import { act, render, screen } from '@testing-library/react'; +// eslint-disable-next-line import/no-extraneous-dependencies +import userEvent from '@testing-library/user-event'; + +import App from './App'; + +const courseId = 'Fake-ID'; + +describe('App', () => { + it('renders collapsible button', () => { + const onSetIsAiTranslations = jest.fn(); + render( + {}} + courseId={courseId} + />, + ); + + expect( + screen.getByText(/Get free translations/), + ).toBeInTheDocument(); + }); + + it('renders App component', async () => { + const onSetIsAiTranslations = jest.fn(); + + render( + {}} + />, + ); + + userEvent.click(screen.getByTestId('app-entry-btn')); + expect(await screen.findByTestId('action-row-btns')).toBeTruthy(); + }); + + it('goes back to previous view', async () => { + const onSetIsAiTranslations = jest.fn(); + render( + {}} + />, + ); + + userEvent.click( + screen.getByText(/Get free translations/), + ); + expect(await screen.findByText(/Translations is not available/)).toBeInTheDocument(); + expect(await screen.findByTestId('action-row-back-btn')).toBeInTheDocument(); + + await act(async () => { + userEvent.click(screen.queryByTestId('action-row-back-btn')); + }); + + expect(onSetIsAiTranslations).toHaveBeenCalled(); + expect(await screen.findByText(/Get free translations/)).toBeInTheDocument(); + expect(screen.queryByText(/Get free translations is not available/)).not.toBeInTheDocument(); + }); + + it('calls closeTranscriptSettings when close button is clicked', async () => { + const onSetIsAiTranslations = jest.fn(); + const onCloseTranscriptSettings = jest.fn(); + render( + , + ); + + userEvent.click( + screen.getByText(/Get free translations/), + ); + expect(await screen.findByText(/Translations is not available/)).toBeInTheDocument(); + expect(await screen.findByTestId('action-row-close-btn')).toBeInTheDocument(); + + await userEvent.click(screen.queryByTestId('action-row-close-btn')); + + expect(onCloseTranscriptSettings).toHaveBeenCalled(); + }); +}); diff --git a/src/XpertLogo.jsx b/src/XpertLogo.jsx new file mode 100644 index 0000000..f17fa81 --- /dev/null +++ b/src/XpertLogo.jsx @@ -0,0 +1,46 @@ +const XpertLogo = () => ( + + + + + + + + + +); + +export default XpertLogo; diff --git a/src/index.jsx b/src/index.jsx index eed76d7..f1f2a24 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -1,34 +1,3 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import { AppProvider, ErrorPage } from '@edx/frontend-platform/react'; -import { - APP_INIT_ERROR, - APP_READY, - subscribe, - initialize, -} from '@edx/frontend-platform'; +import App from './App'; -import { Route, Routes } from 'react-router-dom'; -import initializeStore from './store'; - -const Hello = () =>
Hello world!
; - -subscribe(APP_READY, () => { - ReactDOM.render( - - - } /> - - , - document.getElementById('root'), - ); -}); - -subscribe(APP_INIT_ERROR, (error) => { - ReactDOM.render(, document.getElementById('root')); -}); - -initialize({ - messages: [], - requireAuthenticatedUser: false, -}); +export default App; diff --git a/src/setupTest.js b/src/setupTest.js new file mode 100644 index 0000000..22c352d --- /dev/null +++ b/src/setupTest.js @@ -0,0 +1,2 @@ +import '@testing-library/jest-dom'; +import '@testing-library/jest-dom/extend-expect'; diff --git a/src/store.js b/src/store.js deleted file mode 100644 index 4e30c64..0000000 --- a/src/store.js +++ /dev/null @@ -1,8 +0,0 @@ -import { configureStore } from '@reduxjs/toolkit'; - -export default function initializeStore(preloadedState = undefined) { - return configureStore({ - reducer: {}, - preloadedState, - }); -}