Skip to content

Commit

Permalink
feat: 🎸 Timetables (kolplattformen#12)
Browse files Browse the repository at this point in the history
* feat: 🎸 Upgraded to api v4

* change: Changed hook from useChildlist to useEtjanstChild

* feat: 🎸 Added useSkola24Children

* feat: 🎸 Added useTimetable

* feat: 🎸 Added back hook for useChildList

This hook wraps useEtjanstChildren and useSkola24Children and merges the
results
  • Loading branch information
JohanObrink authored Apr 9, 2021
1 parent 83a4737 commit 2ae212d
Show file tree
Hide file tree
Showing 15 changed files with 1,276 additions and 201 deletions.
26 changes: 13 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,32 @@
"publish-package": "npm publish --access public"
},
"dependencies": {
"react-redux": "^7.2.2",
"react-redux": "^7.2.3",
"redux": "^4.0.5"
},
"peerDependencies": {
"@skolplattformen/embedded-api": "^2.0.0",
"@skolplattformen/embedded-api": "^4.0.0",
"react": "^16.11.0"
},
"devDependencies": {
"@babel/preset-env": "^7.13.10",
"@babel/preset-react": "^7.12.13",
"@babel/preset-env": "^7.13.15",
"@babel/preset-react": "^7.13.13",
"@babel/preset-typescript": "^7.13.0",
"@skolplattformen/embedded-api": "^2.0.0",
"@testing-library/jest-dom": "^5.11.9",
"@testing-library/react": "^11.2.5",
"@testing-library/react-hooks": "^5.1.0",
"@types/jest": "^26.0.20",
"@skolplattformen/embedded-api": "^4.0.0",
"@testing-library/jest-dom": "^5.11.10",
"@testing-library/react": "^11.2.6",
"@testing-library/react-hooks": "^5.1.1",
"@types/jest": "^26.0.22",
"@types/react": "^16.14.3",
"@types/react-redux": "^7.1.16",
"@typescript-eslint/eslint-plugin": "^4.17.0",
"@typescript-eslint/eslint-plugin": "^4.21.0",
"babel-jest": "^26.6.3",
"eslint": "^7.21.0",
"eslint": "^7.23.0",
"eslint-config-airbnb-typescript": "^12.3.1",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jest": "^24.2.1",
"eslint-plugin-jest": "^24.3.4",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-react": "^7.22.0",
"eslint-plugin-react": "^7.23.2",
"eslint-plugin-react-hooks": "^4.2.0",
"events": "^3.3.0",
"jest": "^26.6.3",
Expand Down
2 changes: 2 additions & 0 deletions src/__mocks__/@skolplattformen/embedded-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ const createApi = () => ({

getCalendar: jest.fn(),
getChildren: jest.fn(),
getSkola24Children: jest.fn(),
getClassmates: jest.fn(),
getMenu: jest.fn(),
getNews: jest.fn(),
getNewsDetails: jest.fn(),
getNotifications: jest.fn(),
getSchedule: jest.fn(),
getTimetable: jest.fn(),
getUser: jest.fn(),
})
const init = jest.fn().mockImplementation(() => createApi())
Expand Down
66 changes: 66 additions & 0 deletions src/childlists.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { merge } from './childlists'

describe('childlists', () => {
describe('merge', () => {
it('works with empty skola24children list', () => {
const etjanstChildren = [
{ name: 'Uwe Übrink (elev)' },
{ name: 'Cassius Übrink (elev)' },
]
const skola24Children = []

const children = [
{ name: 'Uwe Übrink (elev)' },
{ name: 'Cassius Übrink (elev)' },
]
expect(merge(etjanstChildren, skola24Children)).toEqual(children)
})
it('works with same length skola24children list', () => {
const etjanstChildren = [
{ name: 'Uwe Übrink (elev)' },
{ name: 'Cassius Übrink (elev)' },
]
const skola24Children = [
{ firstName: 'Uwe', lastName: 'Vredstein Übrink' },
{ firstName: 'Cassius', lastName: 'Vredstein Übrink' },
]

const children = [
{ name: 'Uwe Übrink (elev)', firstName: 'Uwe', lastName: 'Vredstein Übrink' },
{ name: 'Cassius Übrink (elev)', firstName: 'Cassius', lastName: 'Vredstein Übrink' },
]
expect(merge(etjanstChildren, skola24Children)).toEqual(children)
})
it('works with different length skola24children list', () => {
const etjanstChildren = [
{ name: 'Uwe Übrink (elev)' },
{ name: 'Cassius Übrink (elev)' },
]
const skola24Children = [
{ firstName: 'Uwe', lastName: 'Vredstein Übrink' },
]

const children = [
{ name: 'Uwe Übrink (elev)', firstName: 'Uwe', lastName: 'Vredstein Übrink' },
{ name: 'Cassius Übrink (elev)' },
]
expect(merge(etjanstChildren, skola24Children)).toEqual(children)
})
it('works with non matching skola24children list', () => {
const etjanstChildren = [
{ name: 'Uwe Übrink (elev)' },
{ name: 'Cassius Übrink (elev)' },
]
const skola24Children = [
{ firstName: 'Uwe', lastName: 'Vredstein Übrink' },
{ firstName: 'Rolph', lastName: 'Gögendorff Bröök' },
]

const children = [
{ name: 'Uwe Übrink (elev)', firstName: 'Uwe', lastName: 'Vredstein Übrink' },
{ name: 'Cassius Übrink (elev)' },
]
expect(merge(etjanstChildren, skola24Children)).toEqual(children)
})
})
})
15 changes: 15 additions & 0 deletions src/childlists.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Child, EtjanstChild, Skola24Child } from '@skolplattformen/embedded-api'

// eslint-disable-next-line import/prefer-default-export
export const merge = (etjanstChildren: EtjanstChild[], skola24Children: Skola24Child[]): Child[] => (
etjanstChildren.map((etjanstChild) => {
const skola24Child: Skola24Child = (
skola24Children.find((s24c) => s24c.firstName && etjanstChild.name.startsWith(s24c.firstName)) || {}
)
const child: Child = {
...etjanstChild,
...skola24Child,
}
return child
})
)
4 changes: 2 additions & 2 deletions src/fake.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ApiProvider } from '.'
import createStorage from './__mocks__/AsyncStorage'
import {
useCalendar,
useChildList,
useEtjanstChildren,
useClassmates,
useMenu,
useNews,
Expand Down Expand Up @@ -69,7 +69,7 @@ describe('hooks with fake data', () => {
const {
result,
waitForNextUpdate,
} = renderHook(() => useChildList(), { wrapper })
} = renderHook(() => useEtjanstChildren(), { wrapper })

await waitForNextUpdate()
await waitForNextUpdate()
Expand Down
58 changes: 54 additions & 4 deletions src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import {
CalendarItem,
Child,
Classmate,
EtjanstChild,
MenuItem,
NewsItem,
Notification,
ScheduleItem,
Skola24Child,
TimetableEntry,
User,
} from '@skolplattformen/embedded-api'
import {
Expand All @@ -22,6 +25,7 @@ import {
import { useApi } from './context'
import { loadAction } from './actions'
import store from './store'
import { merge } from './childlists'

interface StoreSelector<T> {
(state: EntityStoreRootState): EntityMap<T>
Expand Down Expand Up @@ -73,7 +77,15 @@ const hook = <T>(
}
useEffect(() => { load() }, [isLoggedIn])

let mounted: boolean
useEffect(() => {
mounted = true
return () => { mounted = false }
}, [])

const listener = () => {
if (!mounted) return

const newState = select(getState())
if (newState.status !== state.status
|| newState.data !== state.data
Expand All @@ -94,14 +106,22 @@ const hook = <T>(
}
}

export const useChildList = () => hook<Child[]>(
'CHILDREN',
'children',
export const useEtjanstChildren = () => hook<EtjanstChild[]>(
'ETJANST_CHILDREN',
'etjanst_children',
[],
(s) => s.children,
(s) => s.etjanstChildren,
(api) => () => api.getChildren(),
)

export const useSkola24Children = () => hook<Skola24Child[]>(
'SKOLA24_CHILDREN',
'skola24_children',
[],
(s) => s.skola24Children,
(api) => () => api.getSkola24Children(),
)

export const useCalendar = (child: Child) => hook<CalendarItem[]>(
'CALENDAR',
`calendar_${child.id}`,
Expand Down Expand Up @@ -158,10 +178,40 @@ export const useSchedule = (child: Child, from: string, to: string) => hook<Sche
(api) => () => api.getSchedule(child, from, to),
)

export const useTimetable = (child: Skola24Child, week: number, year: number) => hook<TimetableEntry[]>(
'TIMETABLE',
`timetable_${child.personGuid}_${week}_${year}`,
[],
(s) => s.timetable,
(api) => () => api.getTimetable(child, week, year),
)

export const useUser = () => hook<User>(
'USER',
'user',
{},
(s) => s.user,
(api) => () => api.getUser(),
)

export const useChildList = (): EntityHookResult<Child[]> => {
const {
data: etjanstData, status, error, reload: etjanstReload,
} = useEtjanstChildren()
const { data: skola24Data, reload: skola24Reload } = useSkola24Children()

const [data, setData] = useState<Child[]>([])
const reload = () => {
etjanstReload()
skola24Reload()
}

useEffect(() => {
if (!etjanstData.length) return
setData(merge(etjanstData, skola24Data))
}, [etjanstData, skola24Data])

return {
data, status, error, reload,
}
}
10 changes: 5 additions & 5 deletions src/logout.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'
import { renderHook, act } from '@testing-library/react-hooks'
import { ApiProvider } from './provider'
import { useChildList } from './hooks'
import { useEtjanstChildren } from './hooks'
import store from './store'
import init from './__mocks__/@skolplattformen/embedded-api'
import createStorage from './__mocks__/AsyncStorage'
Expand Down Expand Up @@ -32,7 +32,7 @@ describe('logout - cleanup', () => {
})
))
storage = createStorage({
'123_children': [{ id: 2 }],
'123_etjanst_children': [{ id: 2 }],
}, 2)
})
afterEach(async () => {
Expand All @@ -46,7 +46,7 @@ describe('logout - cleanup', () => {
api.isLoggedIn = true
api.isFake = false

const { waitForNextUpdate: wait1 } = renderHook(() => useChildList(), { wrapper })
const { waitForNextUpdate: wait1 } = renderHook(() => useEtjanstChildren(), { wrapper })

await wait1()
await wait1()
Expand All @@ -56,14 +56,14 @@ describe('logout - cleanup', () => {
api.isLoggedIn = false
api.emitter.emit('logout')

const { result } = renderHook(() => useChildList(), { wrapper })
const { result } = renderHook(() => useEtjanstChildren(), { wrapper })

expect(result.current.data).toHaveLength(0)

api.isLoggedIn = true
api.emitter.emit('login')

const { result: result2, waitForNextUpdate: wait2 } = renderHook(() => useChildList(), { wrapper })
const { result: result2, waitForNextUpdate: wait2 } = renderHook(() => useEtjanstChildren(), { wrapper })

await wait2()

Expand Down
8 changes: 6 additions & 2 deletions src/reducers.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import {
CalendarItem,
Child,
Classmate,
EtjanstChild,
Skola24Child,
MenuItem,
NewsItem,
Notification,
ScheduleItem,
User,
TimetableEntry,
} from '@skolplattformen/embedded-api'
import { EntityName, EntityReducer, EntityState } from './types'

Expand Down Expand Up @@ -65,11 +67,13 @@ const createReducer = <T>(entity: EntityName): EntityReducer<T> => {
}

export const user = createReducer<User>('USER')
export const children = createReducer<Child[]>('CHILDREN')
export const etjanstChildren = createReducer<EtjanstChild[]>('ETJANST_CHILDREN')
export const skola24Children = createReducer<Skola24Child[]>('SKOLA24_CHILDREN')
export const calendar = createReducer<CalendarItem[]>('CALENDAR')
export const classmates = createReducer<Classmate[]>('CLASSMATES')
export const menu = createReducer<MenuItem[]>('MENU')
export const news = createReducer<NewsItem[]>('NEWS')
export const newsDetails = createReducer<NewsItem[]>('NEWS_DETAILS')
export const notifications = createReducer<Notification[]>('NOTIFICATIONS')
export const schedule = createReducer<ScheduleItem[]>('SCHEDULE')
export const timetable = createReducer<TimetableEntry[]>('TIMETABLE')
8 changes: 6 additions & 2 deletions src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,29 @@ import { createStore, combineReducers, applyMiddleware } from 'redux'
import { apiMiddleware, cacheMiddleware } from './middleware'
import {
calendar,
children,
classmates,
etjanstChildren,
menu,
news,
newsDetails,
notifications,
schedule,
skola24Children,
timetable,
user,
} from './reducers'

const appReducer = combineReducers({
calendar,
children,
classmates,
etjanstChildren,
menu,
news,
newsDetails,
notifications,
schedule,
skola24Children,
timetable,
user,
})
const rootReducer: typeof appReducer = (state, action) => {
Expand Down
Loading

0 comments on commit 2ae212d

Please sign in to comment.