Skip to content

Commit

Permalink
Merge pull request #26 from dopplr-labs/feature/toast
Browse files Browse the repository at this point in the history
feature/toast
  • Loading branch information
abinashpanda committed Jan 30, 2021
2 parents bc3341c + 1812e73 commit 97a8b30
Show file tree
Hide file tree
Showing 7 changed files with 473 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/components/message/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { MessageProvider, useMessage } from './message'

export { MessageProvider, useMessage }
24 changes: 24 additions & 0 deletions src/components/message/message-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* istanbul ignore file */
import { createContext } from 'react'
import { MessageOptions } from './message'

export const MessageContext = createContext<{
message: {
// eslint-disable-next-line func-call-spacing
info: (title: string, options?: MessageOptions) => string
success: (title: string, options?: MessageOptions) => string
warning: (title: string, options?: MessageOptions) => string
error: (title: string, options?: MessageOptions) => string
loading: (title: string, options?: MessageOptions) => string
}
removeMessage: (id: string) => void
}>({
message: {
info: () => '',
success: () => '',
warning: () => '',
error: () => '',
loading: () => '',
},
removeMessage: () => {},
})
100 changes: 100 additions & 0 deletions src/components/message/message.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import React, { useMemo } from 'react'
import Button from 'components/button'
import { Meta } from '@storybook/react/types-6-0'
import { CurrencyRupeeOutline } from 'components/icons'
import { MessageProvider, useMessage } from './message'

export default {
title: 'Feedback/Message',
component: MessageProvider,
decorators: [
(Story) => (
<MessageProvider>
<Story />
</MessageProvider>
),
],
} as Meta

export function NormalMessage() {
const { message } = useMessage()

function showToast() {
message.info('This is a normal message')
}

return (
<Button
label="Display Simple Message"
buttonType={Button.ButtonType.primary}
onClick={showToast}
/>
)
}

export function DifferentTypesOfMessages() {
const { message } = useMessage()

function success() {
message.success('This is a success message')
}

function warning() {
message.warning('This is a warning message')
}

function error() {
message.error('This is an error message')
}

return (
<div className="flex items-center space-x-4">
<Button label="Success" onClick={success} />
<Button label="Warning" onClick={warning} />
<Button label="Error" onClick={error} />
</div>
)
}

export function CustomDismissTime() {
const { message } = useMessage()

const success = () => {
message.success(
'This is a prompt message for success, and it will disappear in 10 seconds',
{ dismissTime: 10000 },
)
}

return <Button label="Custom Dismiss Time" onClick={success} />
}

export function DispalyLoadingIndicator() {
const { message } = useMessage()

const loading = () => {
message.loading('Saving changes. Please wait!!')
}

return <Button label="Display Loading Indicator" onClick={loading} />
}

export function MessageWithCustomIcon() {
const { message } = useMessage()

const icon = useMemo(
() => (
<div className="text-green-500">
<CurrencyRupeeOutline />
</div>
),
[],
)

const renderMessage = () => {
message.info('You will recieve refund in 5-7 days!!!', {
icon,
})
}
return <Button label="Message with custom icon" onClick={renderMessage} />
}
119 changes: 119 additions & 0 deletions src/components/message/message.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import React, { useState } from 'react'
import {
render,
screen,
waitForElementToBeRemoved,
} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import Button from 'components/button'
import { CurrencyRupeeOutline } from 'components/icons'
import { MessageProvider, MessageTypes, useMessage } from './message'

type RenderButtonProps = {
type?: MessageTypes
messageContent?: string
dismissTime?: number
icon?: React.ReactElement
}

function renderInMessageProvider(children: React.ReactElement) {
return render(<MessageProvider>{children}</MessageProvider>)
}

function RenderButton({
type = MessageTypes.INFO,
messageContent = 'Hello World',
dismissTime = 10000,
icon,
}: RenderButtonProps) {
const { message, removeMessage } = useMessage()
const [messageId, setMessageId] = useState('')

const renderMessage = () => {
const id = message[type](messageContent, { dismissTime, icon })
setMessageId(id)
}

const deleteMessage = () => {
removeMessage(messageId)
}

return (
<>
<Button label="Click Me" onClick={renderMessage} />
<Button label="Delete Message" onClick={deleteMessage} />
</>
)
}

test('render content inside message correctly', () => {
renderInMessageProvider(<RenderButton />)
userEvent.click(screen.getByText('Click Me'))
expect(screen.getByText('Hello World')).toBeInTheDocument()
})

test('render success variant of message correctly', () => {
renderInMessageProvider(<RenderButton type={MessageTypes.SUCCESS} />)
userEvent.click(screen.getByText('Click Me'))
expect(screen.getByText('Hello World')).toBeInTheDocument()
expect(screen.getByText('Hello World').parentElement?.firstChild).toHaveClass(
'text-green-500',
)
})

test('render warning variant of message correctly', () => {
renderInMessageProvider(<RenderButton type={MessageTypes.WARNING} />)
userEvent.click(screen.getByText('Click Me'))
expect(screen.getByText('Hello World')).toBeInTheDocument()
expect(screen.getByText('Hello World').parentElement?.firstChild).toHaveClass(
'text-yellow-300',
)
})

test('render error variant of message correctly', () => {
renderInMessageProvider(<RenderButton type={MessageTypes.ERROR} />)
userEvent.click(screen.getByText('Click Me'))
expect(screen.getByText('Hello World')).toBeInTheDocument()
expect(screen.getByText('Hello World').parentElement?.firstChild).toHaveClass(
'text-red-500',
)
})

test('render loading variant of message correctly', () => {
renderInMessageProvider(<RenderButton type={MessageTypes.LOADING} />)
userEvent.click(screen.getByText('Click Me'))
expect(screen.getByText('Hello World')).toBeInTheDocument()
expect(screen.getByText('Hello World').parentElement?.firstChild).toHaveClass(
'text-gray-500',
)
})

test('removeMessage function working correctly', async () => {
renderInMessageProvider(<RenderButton />)
userEvent.click(screen.getByText('Click Me'))
expect(screen.getByText('Hello World')).toBeInTheDocument()
userEvent.click(screen.getByText('Delete Message'))
await waitForElementToBeRemoved(() => screen.queryByText('Hello World'))
})

test('message is being removed automatically', async () => {
renderInMessageProvider(<RenderButton dismissTime={500} />)
userEvent.click(screen.getByText('Click Me'))
expect(screen.getByText('Hello World')).toBeInTheDocument()
await waitForElementToBeRemoved(() => screen.queryByText('Hello World'))
})

test('custom icon renders correctly', () => {
const icon = (
<div className="text-green-500">
<CurrencyRupeeOutline />
</div>
)

renderInMessageProvider(<RenderButton icon={icon} />)
userEvent.click(screen.getByText('Click Me'))
expect(screen.getByText('Hello World')).toBeInTheDocument()
expect(screen.getByText('Hello World').parentElement?.firstChild).toHaveClass(
'text-green-500',
)
})
Loading

0 comments on commit 97a8b30

Please sign in to comment.