diff --git a/README.md b/README.md index d351962f92..453b6d2bcb 100644 --- a/README.md +++ b/README.md @@ -514,10 +514,34 @@ Paragon components can have different behavior in the MFE environment. `example` Steps to install the `example` app. +To set up the example app with a local Tutor installation, you'll need to configure a Tutor plugin to handle CORS settings + +Prerequisites. + +Before running the example app, ensure you have a local Tutor installation configured. +1. Create a new file (i.e. `paragon-example-app.py`) in your tutor plugins folder (`tutor plugins printroot` to get the path). +2. Add the following content to the paragon-example-app.py file: +``` +from tutor import hooks + +hooks.Filters.ENV_PATCHES.add_item( + ( + "openedx-lms-development-settings", + """ +CORS_ORIGIN_WHITELIST.append("http://localhost:8080") +LOGIN_REDIRECT_WHITELIST.append("http://localhost:8080") +CSRF_TRUSTED_ORIGINS.append("http://localhost:8080") + """ + ) +) +``` +3. Enable the plugin: `tutor plugins enable paragon-example-app` +4. Apply configuration changes: `tutor config save` && `tutor dev restart` + +Running the Example App: 1. `npm install` to install dependencies. -2. Launch any devstack. It is required for MFE to login into the system and set up configs. -3. `npm run start-example-mfe` to start the app. -4. Go to the `example/src/MyComponent.jsx` and use Paragon components inside the MFE environment. +2. `npm run example:start` to start the app. +3. Go to the `example/src/MyComponent.jsx` and use Paragon components inside the MFE environment. ## Semantic Release diff --git a/example/.env.development b/example/.env.development index 34027f9730..7ff743a905 100644 --- a/example/.env.development +++ b/example/.env.development @@ -1,4 +1,8 @@ -NODE_ENV='development' -REFRESH_ACCESS_TOKEN_ENDPOINT='http://localhost:18000/login_refresh' -LMS_BASE_URL='http://localhost:18000' -ACCESS_TOKEN_COOKIE_NAME='edx-jwt-cookie-header-payload' +NODE_ENV=development +REFRESH_ACCESS_TOKEN_ENDPOINT=http://local.openedx.io:8000/login_refresh +LMS_BASE_URL=http://local.openedx.io:8000 +ACCESS_TOKEN_COOKIE_NAME=edx-jwt-cookie-header-payload +BASE_URL=http://local.openedx.io:8080 +LOGIN_URL=http://local.openedx.io:8000/login +LOGOUT_URL=http://local.openedx.io:8000/logout +CSRF_TOKEN_API_PATH=/csrf/api/v1/token diff --git a/example/public/index.html b/example/public/index.html index c00bf39063..4409e458f5 100644 --- a/example/public/index.html +++ b/example/public/index.html @@ -1,7 +1,7 @@ - Example + Paragon Example diff --git a/example/src/index.jsx b/example/src/index.jsx index 9ce67d7ced..10f45d271f 100644 --- a/example/src/index.jsx +++ b/example/src/index.jsx @@ -1,9 +1,8 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; +import { createRoot } from 'react-dom/client'; import { AppProvider, ErrorPage, - PageRoute, + PageWrap, } from '@edx/frontend-platform/react'; import { APP_INIT_ERROR, APP_READY, initialize } from '@edx/frontend-platform'; import { subscribe } from '@edx/frontend-platform/pubSub'; @@ -11,21 +10,21 @@ import MyComponent from './MyComponent'; import './index.scss'; +const container = document.getElementById('root'); +const root = createRoot(container); + subscribe(APP_READY, () => { - ReactDOM.render( + root.render( - + + + , - document.getElementById('root'), ); }); subscribe(APP_INIT_ERROR, (error) => { - ReactDOM.render(, document.getElementById('root')); + root.render(); }); initialize({ diff --git a/package-lock.json b/package-lock.json index d11752622d..baefa5afd0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15585,9 +15585,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001760", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz", - "integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==", + "version": "1.0.30001764", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001764.tgz", + "integrity": "sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==", "funding": [ { "type": "opencollective", diff --git a/src/Breadcrumb/Breadcrumb.test.jsx b/src/Breadcrumb/Breadcrumb.test.tsx similarity index 75% rename from src/Breadcrumb/Breadcrumb.test.jsx rename to src/Breadcrumb/Breadcrumb.test.tsx index 3b402d971e..4f11c5afbb 100644 --- a/src/Breadcrumb/Breadcrumb.test.jsx +++ b/src/Breadcrumb/Breadcrumb.test.tsx @@ -95,4 +95,29 @@ describe('', () => { expect(links.getAttribute('target')).toBe('_blank'); expect(links.getAttribute('href')).toBe('/link-1'); }); + + it('renders with a custom CSS class', () => { + render(); + const nav = screen.getByLabelText('Breadcrumbs'); + expect(nav.classList).toContain('foobar'); + expect(nav.classList).toContain('pgn__breadcrumb'); + expect(nav.classList).toContain('pgn__breadcrumb-light'); + }); + + it('can access data-xxxxx attributes on the links in clickHandler', async () => { + const user = userEvent.setup(); + const clickHandler = jest.fn(); + render( + clickHandler(currentTarget.dataset.parentIndex)} + />, + ); + const link = screen.getByRole('link', { name: 'Link1' }); + await user.click(link); + expect(clickHandler).toHaveBeenCalledWith('17'); + }); }); diff --git a/src/Breadcrumb/index.tsx b/src/Breadcrumb/index.tsx index 25eed7ff3d..343c922f09 100644 --- a/src/Breadcrumb/index.tsx +++ b/src/Breadcrumb/index.tsx @@ -19,7 +19,7 @@ interface BreadcrumbProps { spacer?: React.ReactElement; /** allows to add a custom function to be called `onClick` of a breadcrumb link. * The use case for this is for adding custom analytics to the component. */ - clickHandler?: (event: React.MouseEvent, link: any) => void; + clickHandler?: (event: React.MouseEvent, link: any) => void; /** The `Breadcrumbs` style variant to use. */ variant?: 'light' | 'dark'; /** The `Breadcrumbs` mobile variant view. */ @@ -28,11 +28,13 @@ interface BreadcrumbProps { * [react-router's Link](https://reactrouter.com/en/main/components/link). */ linkAs?: React.ElementType; + /** Optional class name(s) to append to the base `