Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BA: withUser HOC creation #85

Merged
merged 1 commit into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/authentication/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @baseapp-frontend/authentication

## 2.0.1

### Patch Changes

- Creates `withUser` HOC and `ComponentWithUser` type. `withUser`'s JSDoc contains a more detailed information about its usage.

## 2.0.0

### Major Changes
Expand Down
3 changes: 3 additions & 0 deletions packages/authentication/modules/user/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export { default as getUser } from './getUser'
export { default as useSimpleTokenUser } from './useSimpleTokenUser'
export { default as useUser } from './useUser'

export { default as withUser } from './withUser'
export type * from './withUser/types'
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { FC } from 'react'

import { render } from '@baseapp-frontend/test'
import { IJWTContent } from '@baseapp-frontend/utils'

import withUser from '..'
import { IUser } from '../../../../types/user'
import getUser from '../../getUser'
import { ComponentWithUser } from '../types'

jest.mock('../../getUser', () => jest.fn())

type User = IUser & IJWTContent

const MockComponent: FC<ComponentWithUser<User>> = ({ user }) => (
<div>{user ? `Hello, ${user.firstName}` : 'No user'}</div>
)

describe('withUser HOC', () => {
const getUserMock = getUser as jest.Mock

it('passes the user object to the wrapped component', async () => {
const userMock = { firstName: 'John', lastName: 'Doe' }
getUserMock.mockResolvedValue(userMock)

const WithUserComponent = withUser<User>(MockComponent)
const { findByText } = render(await WithUserComponent({}))

const userElement = await findByText(`Hello, ${userMock.firstName}`)
expect(userElement).toBeInTheDocument()
})

it('handles the case when there is no user', async () => {
getUserMock.mockResolvedValue(null)

const WithUserComponent = withUser<User>(MockComponent)
const { findByText } = render(await WithUserComponent({}))

const userElement = await findByText('No user')
expect(userElement).toBeInTheDocument()
})
})
45 changes: 45 additions & 0 deletions packages/authentication/modules/user/withUser/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { FC } from 'react'

import { IJWTContent } from '@baseapp-frontend/utils'

import getUser from '../getUser'
import { ComponentWithUser } from './types'

/**
* HOC to provide the `user` object to the component.
*
* It's useful when the component that needs to call `getUser` and can't be async (e.g. Client Components).
*
* Consider rendering the Component using the `ComponentWithUser` interface to inherit the `user` type.
* You may need to import the component using dynamic imports with `next/dynamic`.
*
* @example Defining and exporting a component with `withUser`:
* ```tsx
* import { FC } from 'react'
*
* import {ComponentWithUser, withUser} from '@baseapp/authentication'
*
* const MyComponent: FC<ComponentWithUser<MyUser>> = ({user}) => <div>Hi {user.firstName}</div>
*
* export default withUser<MyUser>(MyComponent)
* ```
* @example Importing a component with `withUser` using `next/dynamic`:
* ```tsx
* import dynamic from 'next/dynamic'
*
* const MyComponent = dynamic(() => import('./MyComponent'), { ssr: false })
*
* (...)
* ```
*/
const withUser =
<TUser extends IJWTContent, Props extends object = {}>(
Component: FC<Props & ComponentWithUser<TUser>>,
) =>
async (props: Props) => {
const user = await getUser<TUser>()

return <Component {...props} user={user} />
}

export default withUser
5 changes: 5 additions & 0 deletions packages/authentication/modules/user/withUser/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { IJWTContent } from '@baseapp-frontend/utils'

export interface ComponentWithUser<TUser extends IJWTContent> {
user: TUser | null
}
2 changes: 1 addition & 1 deletion packages/authentication/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@baseapp-frontend/authentication",
"description": "Authentication modules.",
"version": "2.0.0",
"version": "2.0.1",
"main": "./dist/index.ts",
"module": "./dist/index.mjs",
"scripts": {
Expand Down