Skip to content

Commit

Permalink
chore: change toast hook to nanostores
Browse files Browse the repository at this point in the history
  • Loading branch information
Rue-pro committed Mar 20, 2024
1 parent ae2f24e commit 61edddd
Show file tree
Hide file tree
Showing 19 changed files with 170 additions and 98 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
"author": "",
"license": "ISC",
"dependencies": {
"@nanostores/preact": "0.5.1",
"classnames": "2.5.1",
"nanoid": "5.0.6",
"nanostores": "0.10.0",
"preact": "10.19.6"
},
"devDependencies": {
Expand Down
31 changes: 31 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion public/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"message": "Chrome extension for filling anki cards automatically",
"description": "Extension description"
},
"close": {
"CLOSE": {
"message": "Close"
}
}
8 changes: 8 additions & 0 deletions src/__tests__/setupTests.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
import { vi } from 'vitest'

import { chromeMock } from '@shared/browser/__mocks__/chrome'
import * as toastModule from '@shared/ui/Toast'
import { getErrorToastMock } from '@shared/ui/Toast/helpers/__mock__/getErrorToast'
import { addToastMock } from '@shared/ui/Toast/model/__mock__/store'

global.chrome = chromeMock

vi.spyOn(toastModule, 'getErrorToast').mockImplementation(getErrorToastMock)
vi.spyOn(toastModule, 'addToast').mockImplementation(addToastMock)
13 changes: 12 additions & 1 deletion src/app/popup/Popup.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
import { SelectLanguages } from '@features/language/SelectLanguages'

import { LANGUAGES } from '@entities/language'

import { Toasts } from '@shared/ui/Toast'

export const Popup = () => {
return <main>popup</main>
return (
<main>
<SelectLanguages languages={LANGUAGES} />
<Toasts />
</main>
)
}
47 changes: 0 additions & 47 deletions src/shared/ui/Toast/ToastContext.tsx

This file was deleted.

5 changes: 5 additions & 0 deletions src/shared/ui/Toast/helpers/__mock__/getErrorToast.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { MockedFunction, vi } from 'vitest'

import { getErrorToast } from '../getErrorToast'

export const getErrorToastMock: MockedFunction<typeof getErrorToast> = vi.fn()
16 changes: 16 additions & 0 deletions src/shared/ui/Toast/helpers/getErrorToast.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { browser } from '@shared/browser'
import { TBaseError } from '@shared/libs/operationResult'

import { ErrorDetails } from '../../ErrorDetails'
import { IToast } from '../model/types'

export const getErrorToast = (error: TBaseError): Omit<IToast, 'id'> => ({
type: 'error',
title: browser.i18n.getMessage(error.type),
details: (
<ErrorDetails
type={browser.i18n.getMessage(error.type)}
content={error.error?.toString() ?? ''}
/>
),
})
1 change: 1 addition & 0 deletions src/shared/ui/Toast/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { getErrorToast } from './getErrorToast'
4 changes: 3 additions & 1 deletion src/shared/ui/Toast/index.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export { ToastProvider, useToast } from './ToastContext'
export * from './ui'
export * from './model'
export * from './helpers'
5 changes: 5 additions & 0 deletions src/shared/ui/Toast/model/__mock__/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { MockedFunction, vi } from 'vitest'

import { addToast } from '../store'

export const addToastMock: MockedFunction<typeof addToast> = vi.fn()
1 change: 1 addition & 0 deletions src/shared/ui/Toast/model/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { addToast } from './store'
32 changes: 32 additions & 0 deletions src/shared/ui/Toast/model/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { nanoid } from 'nanoid'
import { atom } from 'nanostores'

import { IToast } from './types'

const timeouts: Record<string, NodeJS.Timeout> = {}

export const $toasts = atom<IToast[]>([])

export const addToast = (toast: Omit<IToast, 'id'>) => {
const toastId = nanoid()

$toasts.set([...$toasts.get(), { id: toastId, ...toast }])

timeouts[toastId] = setTimeout(() => {
removeToast(toastId)
}, 2000)
}

export const removeToast = (id: string) => {
clearTimeout(timeouts[id])
delete timeouts[id]

$toasts.set($toasts.get().filter((toast) => toast.id !== id))
}

export const cleanupToasts = () => {
for (const id in timeouts) {
clearTimeout(timeouts[id])
}
$toasts.set([])
}
10 changes: 10 additions & 0 deletions src/shared/ui/Toast/model/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ComponentChildren } from 'preact'

export type TToastType = 'error' | 'info' | 'success'
export interface IToast {
title: string
message?: ComponentChildren
details?: ComponentChildren
id: string
type: TToastType
}
36 changes: 0 additions & 36 deletions src/shared/ui/Toast/toastStore.ts

This file was deleted.

16 changes: 4 additions & 12 deletions src/shared/ui/Toast/Toast.tsx → src/shared/ui/Toast/ui/Toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,11 @@ import { ComponentChildren } from 'preact'

import { browser } from '@shared/browser'

import { Button, TButtonColor } from '../Button'
import { ClearIcon } from '../icons/ClearIcon'
import { Button, TButtonColor } from '../../Button'
import { ClearIcon } from '../../icons/ClearIcon'
import { IToast, TToastType } from '../model/types'
import styles from './styles.module.scss'

export type TToastType = 'error' | 'info' | 'success'
export interface IToast {
title: string
message?: ComponentChildren
details?: ComponentChildren
id: string
type: TToastType
}

interface Props {
toast: IToast
removeToast: () => void
Expand Down Expand Up @@ -56,7 +48,7 @@ export const Toast = ({ toast, removeToast, openDetails }: Props) => {
color={mapToastTypeToButtonColor[type]}
endIcon={<ClearIcon />}
onClick={removeToast}
aria-label={browser.i18n.getMessage('closeToast')}
aria-label={browser.i18n.getMessage('CLOSE')}
/>
</li>
)
Expand Down
37 changes: 37 additions & 0 deletions src/shared/ui/Toast/ui/Toasts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useStore } from '@nanostores/preact'
import { cleanup } from '@testing-library/preact'
import { ComponentChildren } from 'preact'
import { useEffect, useState } from 'preact/hooks'

import { Modal } from '../../Modal'
import { $toasts, removeToast } from '../model/store'
import { Toast } from './Toast'
import styles from './styles.module.scss'

export const Toasts = () => {
const toasts = useStore($toasts)
const [details, setDetails] = useState<ComponentChildren | null>(null)

useEffect(() => {
return () => cleanup()
}, [])

return (
<>
<ul className={styles.toasts}>
{toasts.map((toast) => (
<Toast
key={toast.id}
toast={toast}
removeToast={() => removeToast(toast.id)}
openDetails={setDetails}
/>
))}
</ul>

<Modal open={!!details} onClose={() => setDetails(null)}>
{details}
</Modal>
</>
)
}
1 change: 1 addition & 0 deletions src/shared/ui/Toast/ui/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Toasts } from './Toasts'
File renamed without changes.

0 comments on commit 61edddd

Please sign in to comment.