diff --git a/components/src/components/molecules/CountdownCircle/CountdownCircle.test.tsx b/components/src/components/molecules/CountdownCircle/CountdownCircle.test.tsx
index 9335e31d..55d3b20a 100644
--- a/components/src/components/molecules/CountdownCircle/CountdownCircle.test.tsx
+++ b/components/src/components/molecules/CountdownCircle/CountdownCircle.test.tsx
@@ -2,53 +2,76 @@ import * as React from 'react'
import { ThemeProvider } from 'styled-components'
-import { cleanup, render, screen, waitFor } from '@/test'
+import { act, cleanup, render, screen } from '@/test'
import { CountdownCircle } from './CountdownCircle'
import { lightTheme } from '@/src/tokens'
+const advanceTime = (ms: number) => {
+ act(() => {
+ jest.advanceTimersByTime(ms)
+ })
+}
+
describe('', () => {
+ beforeAll(() => {
+ jest.useFakeTimers()
+ })
+ afterAll(() => {
+ jest.useRealTimers()
+ })
afterEach(cleanup)
- it('renders', async () => {
+ it('renders', () => {
render(
-
+
,
)
+ advanceTime(10000)
expect(screen.getByTestId('countdown-circle')).toBeInTheDocument()
})
- it('should countdown starting from supplied value', async () => {
+ it('should countdown starting from supplied value', () => {
render(
-
+
,
)
- await waitFor(() => {
- expect(screen.queryByText('9')).toBeInTheDocument()
- })
+ advanceTime(1000)
+ expect(screen.queryByText('9')).toBeInTheDocument()
})
- it('should not countdown if disabled', async () => {
+ it('should not countdown if disabled', () => {
render(
-
+
,
)
- await new Promise((r) => setTimeout(r, 2000))
+ advanceTime(1000)
expect(screen.queryByText('10')).toBeInTheDocument()
})
- it('should call callback on 0', async () => {
+ it('should call callback on 0', () => {
const mockCallback = jest.fn()
render(
-
+
+ ,
+ )
+ advanceTime(1000)
+ expect(mockCallback).toHaveBeenCalled()
+ })
+ it('should use startTimestamp if provided', () => {
+ render(
+
+
,
)
- await waitFor(() => {
- expect(mockCallback).toHaveBeenCalled()
- })
+ advanceTime(5000)
+ expect(screen.queryByTestId('countdown-complete-check')).toBeInTheDocument()
})
})
diff --git a/components/src/components/molecules/CountdownCircle/CountdownCircle.tsx b/components/src/components/molecules/CountdownCircle/CountdownCircle.tsx
index 98d67819..fc1ce87b 100644
--- a/components/src/components/molecules/CountdownCircle/CountdownCircle.tsx
+++ b/components/src/components/molecules/CountdownCircle/CountdownCircle.tsx
@@ -4,6 +4,7 @@ import styled, { css } from 'styled-components'
import { VisuallyHidden } from '../..'
import { Colors } from '@/src/tokens'
import { getTestId } from '../../../utils/utils'
+import { CheckSVG } from '@/src/icons'
const CountDownContainer = styled.div(
() => css`
@@ -31,6 +32,12 @@ const NumberBox = styled.div(
color: ${theme.colors.textPlaceholder};
`}
+ #countdown-complete-check {
+ stroke-width: ${theme.borderWidths['1.5']};
+ overflow: visible;
+ display: block;
+ }
+
${() => {
switch ($size) {
case 'small':
@@ -109,8 +116,9 @@ type NativeDivProps = React.HTMLAttributes
type Props = {
accessibilityLabel?: string
- countdownAmount: number
color?: Colors
+ startTimestamp?: number
+ countdownSeconds: number
disabled?: boolean
callback?: () => void
size?: 'small' | 'large'
@@ -122,32 +130,43 @@ export const CountdownCircle = React.forwardRef(
accessibilityLabel,
color = 'textSecondary',
size = 'small',
- countdownAmount,
+ countdownSeconds,
+ startTimestamp,
disabled,
callback,
...props
}: Props,
ref: React.Ref,
) => {
- const [totalCount, setTotalCount] = React.useState(0)
- const [currentCount, setCurrentCount] = React.useState(0)
+ const _startTimestamp = React.useMemo(
+ () => Math.ceil((startTimestamp || Date.now()) / 1000),
+ [startTimestamp],
+ )
+ const endTimestamp = React.useMemo(
+ () => _startTimestamp + countdownSeconds,
+ [_startTimestamp, countdownSeconds],
+ )
+ const calculateCurrentCount = React.useCallback(
+ () => Math.max(endTimestamp - Math.ceil(Date.now() / 1000), 0),
+ [endTimestamp],
+ )
+
+ const [currentCount, setCurrentCount] = React.useState(countdownSeconds)
React.useEffect(() => {
- setTotalCount(countdownAmount)
if (!disabled) {
- setCurrentCount(countdownAmount)
+ setCurrentCount(calculateCurrentCount())
const countInterval = setInterval(() => {
- setCurrentCount((prevCount) => {
- if (prevCount === 1) {
- clearInterval(countInterval)
- callback && callback()
- }
- return prevCount - 1 ? prevCount - 1 : 0
- })
+ const currentSeconds = calculateCurrentCount()
+ if (currentSeconds === 0) {
+ clearInterval(countInterval)
+ callback && callback()
+ }
+ setCurrentCount(currentSeconds)
}, 1000)
return () => clearInterval(countInterval)
}
- }, [callback, countdownAmount, disabled])
+ }, [calculateCurrentCount, callback, countdownSeconds, disabled])
return (
- {disabled ? totalCount : currentCount}
+ {disabled && countdownSeconds}
+ {!disabled &&
+ (currentCount > 0 ? (
+ currentCount
+ ) : (
+
+ ))}
{accessibilityLabel && (
@@ -170,7 +198,7 @@ export const CountdownCircle = React.forwardRef(
cy="12"
fill="none"
r="9"
- strokeDasharray={`${48 * (currentCount / totalCount)}, 56`}
+ strokeDasharray={`${48 * (currentCount / countdownSeconds)}, 56`}
strokeLinecap="round"
/>
+
```
## Props
@@ -19,9 +19,9 @@ import { CountdownCircle } from '@ensdomains/thorin'
```tsx expand=true live=true
-
-
-
+
+
+
```
@@ -29,13 +29,13 @@ import { CountdownCircle } from '@ensdomains/thorin'
```tsx expand=true live=true
-
-
+
+
```
## Disabled
```tsx expand=true live=true
-
+
```
diff --git a/docs/src/reference/snippets/molecules/CountdownCircle.snippets.tsx b/docs/src/reference/snippets/molecules/CountdownCircle.snippets.tsx
index 33786b17..8e98dc7b 100644
--- a/docs/src/reference/snippets/molecules/CountdownCircle.snippets.tsx
+++ b/docs/src/reference/snippets/molecules/CountdownCircle.snippets.tsx
@@ -7,6 +7,6 @@ import { Snippet } from '../../../types'
export const snippets: Snippet[] = [
{
name: 'Basic',
- code: ,
+ code: ,
},
]