Skip to content

Commit

Permalink
Merge pull request #72 from ensdomains/feat/add-countdown-circle-time…
Browse files Browse the repository at this point in the history
…stamp

Add optional startTimestamp to countdownCircle
  • Loading branch information
TateB authored Sep 20, 2022
2 parents 9129799 + b92385e commit ad28095
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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('<CountdownCircle />', () => {
beforeAll(() => {
jest.useFakeTimers()
})
afterAll(() => {
jest.useRealTimers()
})
afterEach(cleanup)

it('renders', async () => {
it('renders', () => {
render(
<ThemeProvider theme={lightTheme}>
<CountdownCircle countdownAmount={10} />
<CountdownCircle countdownSeconds={10} />
</ThemeProvider>,
)
advanceTime(10000)
expect(screen.getByTestId('countdown-circle')).toBeInTheDocument()
})

it('should countdown starting from supplied value', async () => {
it('should countdown starting from supplied value', () => {
render(
<ThemeProvider theme={lightTheme}>
<CountdownCircle countdownAmount={10} />
<CountdownCircle countdownSeconds={10} />
</ThemeProvider>,
)
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(
<ThemeProvider theme={lightTheme}>
<CountdownCircle countdownAmount={10} disabled />
<CountdownCircle countdownSeconds={10} disabled />
</ThemeProvider>,
)
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(
<ThemeProvider theme={lightTheme}>
<CountdownCircle callback={mockCallback} countdownAmount={1} />
<CountdownCircle callback={mockCallback} countdownSeconds={1} />
</ThemeProvider>,
)
advanceTime(1000)
expect(mockCallback).toHaveBeenCalled()
})
it('should use startTimestamp if provided', () => {
render(
<ThemeProvider theme={lightTheme}>
<CountdownCircle
countdownSeconds={10}
startTimestamp={Date.now() - 5000}
/>
</ThemeProvider>,
)
await waitFor(() => {
expect(mockCallback).toHaveBeenCalled()
})
advanceTime(5000)
expect(screen.queryByTestId('countdown-complete-check')).toBeInTheDocument()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down Expand Up @@ -31,6 +32,12 @@ const NumberBox = styled.div<NumberBox>(
color: ${theme.colors.textPlaceholder};
`}
#countdown-complete-check {
stroke-width: ${theme.borderWidths['1.5']};
overflow: visible;
display: block;
}
${() => {
switch ($size) {
case 'small':
Expand Down Expand Up @@ -109,8 +116,9 @@ type NativeDivProps = React.HTMLAttributes<HTMLDivElement>

type Props = {
accessibilityLabel?: string
countdownAmount: number
color?: Colors
startTimestamp?: number
countdownSeconds: number
disabled?: boolean
callback?: () => void
size?: 'small' | 'large'
Expand All @@ -122,32 +130,43 @@ export const CountdownCircle = React.forwardRef(
accessibilityLabel,
color = 'textSecondary',
size = 'small',
countdownAmount,
countdownSeconds,
startTimestamp,
disabled,
callback,
...props
}: Props,
ref: React.Ref<HTMLDivElement>,
) => {
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 (
<CountDownContainer
Expand All @@ -157,7 +176,16 @@ export const CountdownCircle = React.forwardRef(
}}
>
<NumberBox {...{ $size: size, $disabled: disabled }}>
{disabled ? totalCount : currentCount}
{disabled && countdownSeconds}
{!disabled &&
(currentCount > 0 ? (
currentCount
) : (
<CheckSVG
data-testid="countdown-complete-check"
id="countdown-complete-check"
/>
))}
</NumberBox>
<Container $color={color} $disabled={disabled} $size={size} ref={ref}>
{accessibilityLabel && (
Expand All @@ -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"
/>
<circle
Expand Down
14 changes: 7 additions & 7 deletions docs/src/reference/mdx/molecules/CountdownCircle.docs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { CountdownCircle } from '@ensdomains/thorin'
```

```tsx expand=true live=true
<CountdownCircle countdownAmount={30} />
<CountdownCircle countdownSeconds={30} />
```

## Props
Expand All @@ -19,23 +19,23 @@ import { CountdownCircle } from '@ensdomains/thorin'

```tsx expand=true live=true
<DeleteMe flexDirection="row">
<CountdownCircle color="blue" countdownAmount={30} />
<CountdownCircle color="green" countdownAmount={30} />
<CountdownCircle color="indigo" countdownAmount={30} />
<CountdownCircle color="blue" countdownSeconds={30} />
<CountdownCircle color="green" countdownSeconds={30} />
<CountdownCircle color="indigo" countdownSeconds={30} />
</DeleteMe>
```

## Sizes

```tsx expand=true live=true
<DeleteMe flexDirection="row">
<CountdownCircle countdownAmount={30} size="small" />
<CountdownCircle countdownAmount={30} size="large" />
<CountdownCircle countdownSeconds={30} size="small" />
<CountdownCircle countdownSeconds={30} size="large" />
</DeleteMe>
```

## Disabled

```tsx expand=true live=true
<CountdownCircle countdownAmount={30} disabled />
<CountdownCircle countdownSeconds={30} disabled />
```
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ import { Snippet } from '../../../types'
export const snippets: Snippet[] = [
{
name: 'Basic',
code: <CountdownCircle countdownAmount={30} />,
code: <CountdownCircle countdownSeconds={30} />,
},
]

0 comments on commit ad28095

Please sign in to comment.