Skip to content

Commit

Permalink
Merge pull request #236 from CDLUC3/chore/JS-update-main-with-latest-…
Browse files Browse the repository at this point in the history
…development

Chore/js update main with latest development
  • Loading branch information
jupiter007 authored Jan 22, 2025
2 parents 94a8fb1 + b590c78 commit 2211020
Show file tree
Hide file tree
Showing 176 changed files with 11,933 additions and 3,876 deletions.
16 changes: 16 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
NEXT_PUBLIC_REGION="us-west-2"
NEXT_PUBLIC_USER_POOL_ID="us-west-2_FIxM1Z1KE"
NEXT_PUBLIC_POOL_APP_CLIENT_ID="64dhg7i512t4gbncbtiuui1oio"
NEXT_PUBLIC_BASE_URL="http://localhost:3000"
NEXT_PUBLIC_SERVER_ENDPOINT="http://localhost:4000"
NEXT_PUBLIC_GRAPHQL_SERVER_ENDPOINT="http://localhost:4000/graphql"

NEXT_PUBLIC_GRAPHQL_ENDPOINT="http://localhost:4000"
NEXT_PUBLIC_GRAPHQL_SERVER_ENDPOINT="http://localhost:4000/graphql"

# JSON Web Token (JWT) settings
JWT_SECRET=

#Crowdin
CROWDIN_PERSONAL_TOKEN=
CROWDIN_PROJECT_ID=
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ yarn-error.log*
*.tsbuildinfo
next-env.d.ts

tmp.js
tmp.js
58 changes: 58 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,50 @@





### Update
- Update main branch with latest development branch

===============================================================================================================
### Updated
=======
- Updated some pages with toast messages, and updated toast files [#231]
- Added Portuguese translations for newly added translation keys [#231]
- Updated version of `next` to `14.2.22` [#231]
- Updated app/template/[templateId]/section/[section_slug] to hook it into backend data [#217]
- Updated app/template/[templateId]/section/create page to use Remirror text editors, and checkboxes with info popovers [#187]
- Updated DMPEditor to use a skeleton while the text editors are loading, since it can be slow [#187]
- Updated app/template/[templateId]/section/new page to hook it up to backend data, handle errors, and add translations [#189]
- Updated app/template/[templateId] page to hook it up to the backend and handle errors and translations[#206]
- Updated app/[locale]/template page to hook it up to backend and handle errors and translations[#82]
- Updated account/profile, email/email-confirmed, email/verification-failed, and account/connections to use new, shared layout containers [#185]
- Refactored font family usage for consistency.

### Added
- Added Toast Message capabilities using **React Aria Component's use Toast**. [#211]
- Project overview page [#175]
- Moved pages under [locale] folder
- dummy portuguese brazilian translation
- New translation updates [#160]
- Installed next-intl, and created a /messages directory for content, and /i18n directory for next-intl request and routing
- Moved pages under dynamic folder [locale]
- Updated middleware to add correct locale based on path, token, browser preference, or default to english
- Added provider NextIntlClientProvider to layout.tsx
- Added a test page, locale-test, where you can see demos of translations
- Added NextIntlClientProvider to the @/utils/test-utils
- Updated next.config.mjs to use next-intl plugin
- Template Builder: Access Page [#166]
- Created css for sectionContainer, sectionHeader, sectionContent for generic sections
- Updated mobile breadcrumbs css
- Added ability to add 'Other' affiliation in the User Profile page [#170]
- Created create/select-template page and test [#167]
- Created new components for this page
- Created Template Select List Item
- Template Builder: Access Page [#166]
- Created css for sectionContainer, sectionHeader, sectionContent for generic sections
- Updated mobile breadcrumbs css
- Added ability to add 'Other' affiliation in the User Profile page [#170]
- Added new components [#111]
- Clickable interface for Template List
- Clickable interface for Template Edit
Expand All @@ -21,6 +67,18 @@
- Added spacing guidelines to the styleguide
- Cleaned up the styleguide and added additional text to explain the spacing guidelines
- Created SCSS helpers/mixins to generate alot of our font sizes,colours and spacing
- Added `LayoutContainer`, `ContentContainer` and their related styles. [#154]
- Added a responsive size hook for react that will return the window size on
resize. [#154, #110]
- Added a `LayoutWithSidebar` container, along with a related
`SidebarContainer`, and documented their use on the styleguide. [#154, #110]


### Fixed
- Fixed `login` 404 error issue and looping issue in middleware [#194]
- Removed old `app/layout.tsx` page. It was causing errors, since we have one located at `app/[locale]/layout.tsx` now.
- Fixed a failing unit test and build for `confirm-email` component when backend server was not running [#180]
- Removed use of NEXT_PUBLIC_GRAPHQL_ENDPOINT env variable, since it was a duplicate of NEXT_PUBLIC_SERVER_ENDPOINT [#171]

### Added
=======
Expand Down
49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- [Running the App](#running-the-app)
- [Building for Production](#building-for-production)
- [Docker Setup](#docker-setup)
- [Localization](#localization)
- [Environment variables](#environment-variables)
- [Project Structure](#project-structure)
- [Authentication](#authentication)
Expand Down Expand Up @@ -64,6 +65,54 @@ npm install
npm install
```

### Localization
The app is using `next-intl` for internationalization (i18n) with prefix-based routing (i.e., a dynamic [locale] segment will be included in the path, like `/en-US/account/connectsion`). We will be using the four-letter locales, meaning a combination of the two-letter language code and the two-letter country code(i.e., `en_US`).

The `i18n/request.js` loads translation files, and `i18n/routing.js` tells `next-intl` which locales shall be supported.

All content to be translated will be located under the `messages` directory, with content being separated out under locales, `messages/en-US/errors.json` and files being organized by components, `messages/en-US/home.json`.

The `NextIntlClientProvider` is wrapped around the children in layout.tsx
```
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
```

This allows components to reference the content from a page, like:
```
import {useTranslations} from 'next-intl';
import {Link} from '@/i18n/routing';
export default function HomePage() {
const t = useTranslations('HomePage');
return (
<div>
<h1>{t('title')}</h1>
<Link href="/about">{t('about')}</Link>
</div>
);
}
```

For more documentation on what is available in `next-intl`, go to: https://next-intl-docs.vercel.app/docs/getting-started

#### Crowdin - Localization Management Platform
We have a free, open-source license with Crowdin: http://crowdin.com.

In the app, we are using the Crowdin Cli to upload content, and download translated files from the site.

You can log into the site with the `dmsp-translate` account, and navigate to `https://crowdin.com/project/dmptool` to see the source files and translations.

In order to upload new changes to the content, run the following scripts from package.json:
- `crowdin:upload` - to upload content changes in english
- `crowdin:download` - to download any new translations

For more documentation on Crowdin, go to:
- https://crowdin.com/
- https://crowdin.github.io/crowdin-cli/


### Environment Variables
For the development environment, the environment variables are stored at `.env.local`. This is set as the default env file in jest.setup.ts.
These variables must be set in order for the app to work.
Expand Down
34 changes: 27 additions & 7 deletions __tests__/middleware.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import {NextRequest, NextResponse} from 'next/server';
import {middleware} from '../middleware';
import {verifyJwtToken} from '@/lib/server/auth';
import {getAuthTokenServer} from '@/utils/getAuthTokenServer';


jest.mock('next/server', () => ({
NextResponse: {
Expand All @@ -8,6 +11,21 @@ jest.mock('next/server', () => ({
},
}));

jest.mock('next-intl/middleware', () => ({
__esModule: true,
default: jest.fn(() => jest.fn()), // Mocks `createMiddleware` returning a handler
}));

jest.mock('@/lib/server/auth', () => ({
verifyJwtToken: jest.fn(),
}));
jest.mock('@/utils/getAuthTokenServer', () => ({
getAuthTokenServer: jest.fn(),
}));

(verifyJwtToken as jest.Mock).mockResolvedValue({ languageId: 'en-US' });
(getAuthTokenServer as jest.Mock).mockResolvedValue('mock-token');

describe('middleware', () => {
let request: NextRequest;
let response: NextResponse;
Expand Down Expand Up @@ -37,13 +55,15 @@ describe('middleware', () => {
(NextResponse.next as jest.Mock).mockReturnValue(response);
});

it('should return next response when it is an excluded path', async () => {
request.nextUrl.pathname = '/_next';
it('should handle locale resolution correctly', async () => {
request.nextUrl.pathname = '/';
(getAuthTokenServer as jest.Mock).mockResolvedValue('mock-token');
(verifyJwtToken as jest.Mock).mockResolvedValue({ languageId: 'pt-BR' });

const result = await middleware(request);
await middleware(request);

expect(NextResponse.next).toHaveBeenCalled();
expect(result).toBe(response);
const expectedUrl = new URL("http://localhost/pt-BR/");
expect(NextResponse.redirect).toHaveBeenCalledWith(expectedUrl);
});

it('should redirect to /login if both tokens are missing and path is protected', async () => {
Expand All @@ -57,7 +77,7 @@ describe('middleware', () => {
});

it('should set custom header for /dmps path', async () => {
request.nextUrl.pathname = '/dmps';
request.nextUrl.pathname = '/en-US/dmps';

const result = await middleware(request);

Expand All @@ -67,7 +87,7 @@ describe('middleware', () => {
});

it('should not redirect if at least one token is present on protected path', async () => {
request.nextUrl.pathname = '/protected';
request.nextUrl.pathname = '/en-US/protected';
request.cookies.get = jest.fn().mockImplementation((key) => {
if (key === 'dmspt') return 'accessToken'; // Simulate accessToken is present
return undefined;
Expand Down
File renamed without changes.
File renamed without changes.
70 changes: 70 additions & 0 deletions app/[locale]/account/connections/__tests__/page.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React, {ReactNode} from 'react';
import {act, render, screen, waitFor} from '@testing-library/react';
import {axe, toHaveNoViolations} from 'jest-axe';
import ConnectionsPage from '../page';

expect.extend(toHaveNoViolations);

jest.mock('@/components/PageHeader', () => {
const mockPageHeader = jest.fn(({ children }: { children: ReactNode, title: string }) => (
<div data-testid="mock-page-wrapper">{children}</div>
));
return {
__esModule: true,
default: mockPageHeader
}
});


describe('Connections page', () => {
beforeEach(() => {
jest.clearAllMocks();
});

afterEach(() => {
jest.restoreAllMocks();
})

it('should render the component with PageHeader', async () => {
const titleProp = 'Connections';
const pageHeader = await import('@/components/PageHeader');
const mockPageHeader = pageHeader.default;
const { getByTestId } = render(<ConnectionsPage />);

expect(getByTestId('mock-page-wrapper')).toBeInTheDocument();
expect(mockPageHeader).toHaveBeenCalledWith(expect.objectContaining({ title: titleProp, }), {})
})

it('should render connections page', async () => {

await act(async () => {
render(
<ConnectionsPage />

);
});

const heading4Elements = screen.getAllByRole('heading', { level: 2 });
expect(heading4Elements.length).toBe(3);

const buttons = screen.getAllByRole('button');
expect(buttons.length).toBe(3);
});

it('should pass axe accessibility test', async () => {
let container: HTMLElement;
await act(async () => {
const renderResult = render(

<ConnectionsPage />

);
container = renderResult.container;
});

await waitFor(async () => {
const results = await axe(container);
expect(results).toHaveNoViolations();
});
})
})
60 changes: 60 additions & 0 deletions app/[locale]/account/connections/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
'use client'

import React from 'react';
import sanitizeHtml from 'sanitize-html';
import ConnectionSection from '@/components/ConnectionSection';
import PageHeader from '@/components/PageHeader';
import {ContentContainer, LayoutContainer,} from '@/components/Container';

const REDIRECT_URI = process.env.NEXT_PUBLIC_ORCID_DEV_CALLBACK;
const ORCID_CLIENT_ID = process.env.NEXT_PUBLIC_ORCID_CLIENT_ID;

const ConnectionsPage: React.FC = () => {

// Sandbox Uri
//const orcidUri = `https://sandbox.orcid.org/oauth/authorize?client_id=${orcidClientId}&response_type=code&scope=/read-limited&redirect_uri=${redirectURI}`;

//Production Uri
const orcidUri = `https://orcid.org/oauth/authorize?client_id=${ORCID_CLIENT_ID}&response_type=code&scope=/authenticate&redirect_uri=${REDIRECT_URI}`;

const orcidContentString = sanitizeHtml('ORCID provides a persistent identifier - an ORCID iD - that distinguishes you from other users. Learn more at <a href="https://orcid.org/" target="_blank" rel="noopener noreferrer">ORCID.org</a>.');

return (
<>
<PageHeader title="Connections" />
<LayoutContainer>
<ContentContainer>
<div className="sectionContainer">
<div className="sectionContent">
<ConnectionSection
type='orcid'
title='ORCID iD - not connected'
content={orcidContentString}
btnUrl={orcidUri}
btnImageUrl='/images/orcid.svg'
btnText='Connect your ORCID iD'
/>
<ConnectionSection
type='orcidtest'
title='ORCiD state when user is connected'
content='This is to test the display of the orcid id once the user has connected.'
btnUrl='/users/auth/orcid/test'
btnImageUrl='/images/orcid.svg'
btnText='Connect your ORCID iD Test'
/>
<ConnectionSection
type='sso'
title='Single Sign On'
content='Connect your account so that you can log into DMP Tool via your institution.'
btnUrl=''
btnText='Connect institutional credentials'
/>
</div>
</div>
</ContentContainer>
</LayoutContainer>
</>
)
}

export default ConnectionsPage;
File renamed without changes.
Loading

0 comments on commit 2211020

Please sign in to comment.