-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
- Loading branch information
There are no files selected for viewing
This file was deleted.
This file was deleted.
Large diffs are not rendered by default.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
This file was deleted.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
This file was deleted.
This file was deleted.
Large diffs are not rendered by default.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
"name": "@doist/reactist", | ||
"description": "Open source React components by Doist", | ||
"author": "Henning Muszynski <[email protected]> (http://doist.com)", | ||
"version": "7.1.9", | ||
"version": "7.2.0", | ||
"license": "MIT", | ||
"homepage": "https://github.com/Doist/reactist#readme", | ||
"repository": "git+https://github.com/Doist/reactist.git", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
@import '../styles/constants.less'; | ||
|
||
.container-style() { | ||
box-sizing: border-box; | ||
justify-content: space-between; | ||
align-items: flex-start; | ||
padding: 10px 14px; | ||
border: 1px solid @color_border_grey; | ||
border-radius: @default-radius; | ||
box-shadow: @default-box-shadow; | ||
background: #fff; | ||
} | ||
|
||
.reactist-notification { | ||
display: flex; | ||
position: relative; | ||
font-size: @normal_font_size; | ||
|
||
&:not(.reactist-notification--with-button) { | ||
.container-style(); | ||
} | ||
} | ||
|
||
.reactist-notification__button, | ||
.reactist-notification__close-button { | ||
border: 0; | ||
padding: 0; | ||
margin: 0; | ||
cursor: pointer; | ||
transition-property: background-color; | ||
transition-duration: @standard-timing; | ||
background: #fff; | ||
|
||
&:hover { | ||
background: @color_grey_10; | ||
} | ||
} | ||
|
||
.reactist-notification__button { | ||
.container-style(); | ||
display: flex; | ||
flex: 1; | ||
|
||
&:hover + .reactist-notification__close-button { | ||
background: @color_grey_10; | ||
} | ||
} | ||
|
||
.reactist-notification__close-button { | ||
position: absolute; | ||
top: 8px; | ||
right: 8px; | ||
border-radius: @default-radius; | ||
|
||
> * { | ||
display: block; | ||
} | ||
} | ||
|
||
.reactist-notification__icon-content-group { | ||
display: flex; | ||
flex: 1; | ||
} | ||
|
||
.reactist-notification--with-close-button { | ||
.reactist-notification__icon-content-group { | ||
padding-right: 24px; | ||
} | ||
} | ||
|
||
.reactist-notification__content { | ||
display: flex; | ||
flex-direction: column; | ||
align-items: flex-start; | ||
} | ||
|
||
.reactist-notification__title { | ||
margin: 0 0 3px 0; | ||
font-size: @normal_font_size; | ||
text-align: left; | ||
} | ||
|
||
.reactist-notification__subtitle { | ||
font-size: @smaller_font_size; | ||
margin: 0; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import * as React from 'react' | ||
import { fireEvent, render, screen } from '@testing-library/react' | ||
|
||
import { Notification } from './notification' | ||
|
||
describe('Notification', () => { | ||
it('renders the provided title and subtitle', () => { | ||
render( | ||
<Notification id="notification-test" title="I'm a title" subtitle="I'm a subtitle" />, | ||
) | ||
|
||
expect(screen.getByText("I'm a title")).toBeVisible() | ||
expect(screen.getByText("I'm a subtitle")).toBeVisible() | ||
}) | ||
|
||
it("doesn't render a button or close button by default", () => { | ||
render( | ||
<Notification id="notification-test" title="I'm a title" subtitle="I'm a subtitle" />, | ||
) | ||
|
||
expect(screen.queryAllByRole('button')).toHaveLength(0) | ||
}) | ||
|
||
it('renders a close button when onClose is provided', () => { | ||
const onClose = jest.fn() | ||
|
||
render( | ||
<Notification | ||
id="notification-test" | ||
title="I'm a title" | ||
onClose={onClose} | ||
closeAltText="Close me" | ||
/>, | ||
) | ||
fireEvent.click(screen.getByRole('button', { name: 'Close me' })) | ||
|
||
expect(onClose).toHaveBeenCalledTimes(1) | ||
}) | ||
|
||
it('renders a button around the content when onClick is provided', () => { | ||
const onClick = jest.fn() | ||
|
||
render(<Notification id="notification-test" title="I'm a title" onClick={onClick} />) | ||
fireEvent.click(screen.getByRole('button', { name: "I'm a title" })) | ||
|
||
expect(onClick).toHaveBeenCalledTimes(1) | ||
}) | ||
|
||
it('renders a provided custom icon', () => { | ||
render( | ||
<Notification | ||
id="notification-test" | ||
title="I'm a title" | ||
icon={<div>I'm an icon</div>} | ||
/>, | ||
) | ||
|
||
expect(screen.getByText("I'm an icon")).toBeVisible() | ||
expect(screen.getByText("I'm a title")).toBeVisible() | ||
}) | ||
|
||
it('renders children in place of the title and subtitle', () => { | ||
render( | ||
<Notification | ||
id="notification-test" | ||
title="I'm a title" | ||
subtitle="I'm a subtitle" | ||
icon={<div>I'm an icon</div>} | ||
> | ||
I'm what gets rendered instead | ||
</Notification>, | ||
) | ||
|
||
expect(screen.queryByText("I'm a title")).not.toBeInTheDocument() | ||
expect(screen.queryByText("I'm a subtitle")).not.toBeInTheDocument() | ||
expect(screen.getByText("I'm an icon")).toBeVisible() | ||
expect(screen.getByRole('dialog', { name: "I'm what gets rendered instead" })).toBeVisible() | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import React from 'react' | ||
import classNames from 'classnames' | ||
import CloseIcon from '../icons/CloseIcon.svg' | ||
import './notification.less' | ||
|
||
type NotificationProps = { | ||
id: string | ||
icon?: React.ReactNode | ||
title?: React.ReactNode | ||
subtitle?: React.ReactNode | ||
children?: React.ReactNode | ||
customCloseButton?: React.ReactNode | ||
onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void | ||
onClose?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void | ||
closeAltText?: string | ||
className?: string | ||
} & JSX.IntrinsicElements['div'] | ||
|
||
function Notification({ | ||
id, | ||
icon, | ||
title, | ||
subtitle, | ||
children, | ||
customCloseButton, | ||
onClick, | ||
onClose, | ||
closeAltText = 'Close', | ||
className, | ||
...rest | ||
}: NotificationProps) { | ||
const titleId = title ? `${id}-title` : null | ||
const titleIdAttribute = titleId ? { id: titleId } : null | ||
const subtitleId = subtitle ? `${id}-subtitle` : null | ||
const subtitleIdAttribute = subtitleId ? { id: subtitleId } : null | ||
const contentId = children ? `${id}-content` : null | ||
const contentIdAttribute = children ? { id: `${id}-content` } : null | ||
const ariaLabelledBy = contentId | ||
? { 'aria-labelledby': contentId } | ||
: titleId | ||
? { 'aria-labelledby': titleId } | ||
: null | ||
const ariaDescribedBy = subtitleId && !children ? { 'aria-describedby': subtitleId } : null | ||
|
||
const notificationContent = ( | ||
<div className="reactist-notification__content" {...contentIdAttribute}> | ||
{children ?? ( | ||
<> | ||
{title ? ( | ||
<h3 className="reactist-notification__title" {...titleIdAttribute}> | ||
{title} | ||
</h3> | ||
) : null} | ||
{subtitle ? ( | ||
<p className="reactist-notification__subtitle" {...subtitleIdAttribute}> | ||
{subtitle} | ||
</p> | ||
) : null} | ||
</> | ||
)} | ||
</div> | ||
) | ||
const notificationBody = ( | ||
<div className="reactist-notification__icon-content-group"> | ||
{icon ?? null} | ||
{notificationContent} | ||
</div> | ||
) | ||
|
||
return ( | ||
<div | ||
id={id} | ||
role="dialog" | ||
className={classNames('reactist-notification', className, { | ||
'reactist-notification--with-button': Boolean(onClick), | ||
'reactist-notification--with-close-button': Boolean(onClose), | ||
})} | ||
{...ariaLabelledBy} | ||
{...ariaDescribedBy} | ||
{...rest} | ||
> | ||
{onClick ? ( | ||
<button className="reactist-notification__button" onClick={onClick}> | ||
{notificationBody} | ||
</button> | ||
) : ( | ||
notificationBody | ||
)} | ||
|
||
{onClose ? ( | ||
<button | ||
className="reactist-notification__close-button" | ||
onClick={onClose} | ||
aria-label={closeAltText} | ||
> | ||
{customCloseButton ?? <CloseIcon />} | ||
</button> | ||
) : null} | ||
</div> | ||
) | ||
} | ||
|
||
export { Notification } |