Skip to content

Commit

Permalink
Merge pull request #327 from HASKI-RAK/118-create-news-banner-component
Browse files Browse the repository at this point in the history
118 create news banner component
  • Loading branch information
Platura authored Sep 12, 2024
2 parents 5c01b75 + 69fad0c commit c2616ce
Show file tree
Hide file tree
Showing 27 changed files with 696 additions and 86 deletions.
22 changes: 22 additions & 0 deletions jest.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,28 @@ const mockDataServices: MockDataServices = {
timecompleted: '1699967821'
}
])
),
fetchNews: jest.fn(() =>
Promise.resolve({
news: [
{
date: 'Thu, 13 Jul 2023 16:00:00 GMT',
expiration_date: 'Sun, 20 Apr 2025 16:00:00 GMT',
id: 1,
language_id: 'en',
news_content: 'We are currently testing the site',
university: 'TH-AB'
},
{
date: 'Thu, 13 Jul 2023 16:00:00 GMT',
expiration_date: 'Sun, 20 Apr 2025 16:00:00 GMT',
id: 2,
language_id: 'en',
news_content: 'We are currently testing the site',
university: 'TH-AB'
}
]
})
)
}
/**
Expand Down
3 changes: 3 additions & 0 deletions src/common/components/DefaultCollapse/DefaultCollapse.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import DefaultCollapse from '@mui/material/Collapse'

export { DefaultCollapse as Collapse }
1 change: 1 addition & 0 deletions src/common/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export { Stack } from './DefaultStack/DefaultStack'
export { Skeleton } from './DefaultSkeleton/DefaultSkeleton'
export { Backdrop } from './DefaultBackdrop/DefaultBackdrop'
export { CircularProgress } from './DefaultCircularProgress/DefaultCircularProgress'
export { Collapse } from './DefaultCollapse/DefaultCollapse'
export { InputAdornment } from './DefaultInputAdornment/DefaultInputAdornment'
export { Paper } from './DefaultPaper/DefaultPaper'
export { Fade } from './DefaultFade/DefaultFade'
Expand Down
31 changes: 31 additions & 0 deletions src/common/hooks/University/University.hooks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import log from 'loglevel'
import { usePersistedStore } from '@store'
import { useTranslation } from 'react-i18next'
import { useState, useEffect, useMemo } from 'react'

export type UniversityHookReturn = {
readonly university: string
}

export const useUniversity = (): UniversityHookReturn => {
const { t } = useTranslation()
const [university, setUniversity] = useState('')
const getUser = usePersistedStore((state) => state.getUser)
useEffect(() => {
//fetch the university from the current user and return university
getUser()
.then((user) => {
setUniversity(user.university)
})
.catch((error) => {
log.error(t('error.getUser') + ' ' + error)
setUniversity('')
})
}, [])
return useMemo(
() => ({
university
}),
[university]
)
}
39 changes: 39 additions & 0 deletions src/common/hooks/University/University.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import '@testing-library/jest-dom'
import { renderHook, waitFor } from '@testing-library/react'
import { mockServices } from 'jest.setup'
import { MemoryRouter } from 'react-router-dom'
import { useUniversity } from '@common/hooks'

test('useUniversity returns valid value', async () => {
mockServices.fetchUser = jest.fn().mockImplementationOnce(() =>
Promise.resolve({
id: 1,
lms_user_id: 1,
name: 'Thaddäus Tentakel',
role: 'Tester',
role_id: 1,
settings: {
id: 1,
user_id: 1,
pswd: '1234',
theme: 'test'
},
university: 'TH-AB'
})
)

const { result } = renderHook(() => useUniversity(), {
wrapper: ({ children }) => <MemoryRouter>{children}</MemoryRouter>
})
await waitFor(() => {
expect(result.current.university).toStrictEqual('TH-AB')
})
})

test('useUniversity returns empty string when fetch fails', async () => {
mockServices.fetchUser = jest.fn().mockImplementationOnce(() => Promise.reject(new Error('error')))
const { result } = renderHook(() => useUniversity(), {
wrapper: ({ children }) => <MemoryRouter>{children}</MemoryRouter>
})
expect(await result.current.university).toBe('')
})
1 change: 1 addition & 0 deletions src/common/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { useTheme } from './DefaultUseTheme/DefaultUseTheme'
export { useMediaQuery } from './DefaultMediaQuery/DefaultMediaQuery'
export { useLearningPathTopicProgress } from './LearningPathTopicProgress/LearningPathTopicProgress.hooks'
export * from './University/University.hooks'
1 change: 0 additions & 1 deletion src/common/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { HaskiTheme } from './Theme/HaskiTheme'
import { Theme } from './Theme/Theme'
import { reportWebVitals, sendToAnalytics } from './Webvitals/Webvitals'

export { reportWebVitals, sendToAnalytics, Theme, HaskiTheme }
63 changes: 63 additions & 0 deletions src/components/Newsbanner/Newsbanner.hooks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import log from 'loglevel'
import { useMemo, useState, useContext, useEffect } from 'react'
import { SnackbarContext } from '@services'
import { useSessionStore } from '@store'
import { useTranslation } from 'react-i18next'
import { useUniversity } from '@common/hooks'
import i18next from 'i18next'

/**
* @prop sets the newsItem if there is atleast one news
* and returns a string of all news
* @prop hasItem - check if there are any news
* @category Hooks
* @interface
*/

export type NewsbannerHookReturn = {
readonly isNewsAvailable: boolean
readonly newsText: string
}

/**
* useNewsbanner hook.
* @remarks
* Hook for the Newsbanner logic.
* first checks if there are any news and then returns a string with all news items.
* also changes news if the language gets changed
*
* @returns - hasItem and all news as a string
*
* @category Hooks
*/

export const useNewsbanner = (): NewsbannerHookReturn => {
const { t, i18n } = useTranslation()
const { addSnackbar } = useContext(SnackbarContext)
const getNews = useSessionStore((state) => state.getNews)
const { university } = useUniversity()
const [isNewsAvailable, setIsNewsAvailable] = useState(false)
const [newsText, setNewsText] = useState('')

//** Logic **/
//returns combined string of all the news
//and checks if there are news
useEffect(() => {
getNews(i18n.language, university)
.then((response) => {
setIsNewsAvailable(response.news.length != 0)
setNewsText(response.news.map(({ news_content }) => news_content).join(', '))
})
.catch((error) => {
addSnackbar({
message: t('error.getNews'),
severity: 'error',
autoHideDuration: 3000
})
log.error(t('error.getNews') + ' ' + error)
setNewsText('')
})
}, [i18next.language])

return useMemo(() => ({ isNewsAvailable, newsText }), [isNewsAvailable, newsText])
}
131 changes: 131 additions & 0 deletions src/components/Newsbanner/Newsbanner.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import '@testing-library/jest-dom'
import { fireEvent, render, act, renderHook } from '@testing-library/react'
import { mockServices } from 'jest.setup'
import Newsbanner from './Newsbanner'
import { MemoryRouter } from 'react-router-dom'
import { useUniversity } from '@common/hooks'

describe('Newsbanner tests', () => {
beforeEach(() => {
window.sessionStorage.clear()
})

test('Newsbanner is open', async () => {
mockServices.fetchNews = jest.fn().mockImplementation(() =>
Promise.resolve({
news: [
{
date: 'Thu, 13 Jul 2023 16:00:00 GMT',
expiration_date: 'Sun, 20 Apr 2025 16:00:00 GMT',
id: 1,
language_id: 'de',
news_content: 'Wir testen die Seite',
university: 'TH-AB'
}
]
})
)

const { getByTestId, rerender } = render(
<MemoryRouter>
<Newsbanner />
</MemoryRouter>
)

await new Promise(process.nextTick)

rerender(
<MemoryRouter>
<Newsbanner />
</MemoryRouter>
)

await new Promise(process.nextTick)

rerender(
<MemoryRouter>
<Newsbanner />
</MemoryRouter>
)
await act(async () => {
//expect(rerender).toContain('Wir testen die Seite')
const closeButton = getByTestId('NewsBannerCloseButton')
expect(closeButton).toBeInTheDocument()
})
})

test('Close the open Newsbanner', async () => {
mockServices.fetchNews.mockImplementation(() =>
Promise.resolve({
news: [
{
date: 'Thu, 13 Jul 2023 16:00:00 GMT',
expiration_date: 'Sun, 20 Apr 2025 16:00:00 GMT',
id: 1,
language_id: 'en',
news_content: 'We are currently testing the site',
university: 'TH-AB'
}
]
})
)

const { container, getByTestId, rerender } = render(
<MemoryRouter>
<Newsbanner />
</MemoryRouter>
)

await new Promise(process.nextTick)

rerender(
<MemoryRouter>
<Newsbanner />
</MemoryRouter>
)

await new Promise(process.nextTick)

rerender(
<MemoryRouter>
<Newsbanner />
</MemoryRouter>
)

await act(async () => {
const closeButton = getByTestId('NewsBannerCloseButton')
fireEvent.click(closeButton)
})

expect(container).toBeEmptyDOMElement()
})

test('Newsbanner has an error when fetching the News', () => {
mockServices.fetchNews.mockImplementationOnce(() => {
throw new Error('Error')
})
const { container } = render(<Newsbanner />)
expect(container).toBeEmptyDOMElement()
})

test('Newsbanner has an error when fetching the University', async () => {
mockServices.fetchUser.mockImplementationOnce(() => {
throw new Error('Error')
})

const { result } = renderHook(() => useUniversity(), {
wrapper: ({ children }) => <MemoryRouter>{children}</MemoryRouter>
})
expect(await result.current.university).toBe('')
})

test('Newsbanner doesnt open because no news', () => {
mockServices.fetchNews.mockImplementationOnce(() =>
Promise.resolve({
news: [{}]
})
)
const { container } = render(<Newsbanner />)
expect(container).toBeEmptyDOMElement()
})
})
Loading

0 comments on commit c2616ce

Please sign in to comment.