Skip to content

Commit

Permalink
add PersistantNotification component, stories and tests (#3921)
Browse files Browse the repository at this point in the history
Co-authored-by: Will McVay <[email protected]>
  • Loading branch information
jblok and willmcvay authored Apr 9, 2021
1 parent 44fdc5e commit 65f139e
Show file tree
Hide file tree
Showing 6 changed files with 337 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { css } from 'linaria'
import { styled } from 'linaria/react'
import { ElIcon } from '../../Icon/__styles__'
import { elIsActive } from '../../../styles-v3/base/states'
import {
elIntentPrimary,
elIntentSecondary,
elIntentCritical,
elIntentSuccess,
elIntentDanger,
} from '../../../styles-v3/base/intent'

export const elPnIcon = css`
padding: 0 0.5rem;
display: flex;
align-items: center;
border-radius var(--default-border-radius) 0 0 var(--default-border-radius);
cursor: pointer;
${ElIcon} {
color: var(--color-white);
}
`

export const elPnContent = css`
padding: 0.75rem 1.25rem;
opacity: 0;
transition: 0.5s;
`

export const ElPersistantNotification = styled.div`
display: flex;
position: fixed;
top: 1rem;
right: 2rem; // should be the width of the elPnIcon element (icon is 1rem and padding is 0.5rem each side)
max-width: 50%;
transform: translateX(100%);
transition: 0.5s;
z-index: 10;
&${elIsActive} {
right: 0;
transform: translateX(0);
.${elPnContent} {
opacity: 1;
}
}
&${elIntentPrimary} {
.${elPnContent} {
background: var(--intent-primary-light);
color: var(--intent-primary-light-text);
}
.${elPnIcon} {
background: var(--intent-primary);
color: var(--intent-primary-text);
}
}
&${elIntentSecondary} {
.${elPnContent} {
background: var(--intent-secondary-light);
color: var(--intent-secondary-light-text);
}
.${elPnIcon} {
background: var(--intent-secondary);
color: var(--intent-secondary-text);
}
}
&${elIntentCritical} {
.${elPnContent} {
background: var(--intent-critical-light);
color: var(--intent-critical-light-text);
}
.${elPnIcon} {
background: var(--intent-critical);
color: var(--intent-critical-text);
}
}
&${elIntentSuccess} {
.${elPnContent} {
background: var(--intent-success-light);
color: var(--intent-success-light-text);
}
.${elPnIcon} {
background: var(--intent-success);
color: var(--intent-success-text);
}
}
&${elIntentDanger} {
.${elPnContent} {
background: var(--intent-danger-light);
color: var(--intent-danger-light-text);
}
.${elPnIcon} {
background: var(--intent-danger);
color: var(--intent-danger-text);
}
}
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`PersistantNotification component should match a snapshot 1`] = `
<ElPersistantNotification
className="el-intent-secondary"
>
<div
className="el-pn-icon"
onClick={[Function]}
>
<Icon
icon="info"
/>
</div>
<div
className="el-pn-content"
>
I am notification
</div>
</ElPersistantNotification>
`;

exports[`PersistantNotification component should match a snapshot when expanded 1`] = `
<ElPersistantNotification
className="el-intent-secondary el-is-active"
>
<div
className="el-pn-icon"
onClick={[Function]}
>
<Icon
icon="info"
/>
</div>
<div
className="el-pn-content"
>
I am notification
</div>
</ElPersistantNotification>
`;

exports[`PersistantNotification component should match a snapshot when given an intent 1`] = `
<ElPersistantNotification
className="el-intent-critical"
>
<div
className="el-pn-icon"
onClick={[Function]}
>
<Icon
icon="info"
/>
</div>
<div
className="el-pn-content"
>
I am notification
</div>
</ElPersistantNotification>
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as React from 'react'
import { shallow } from 'enzyme'
import { PersistantNotification } from '../'
import { elPnIcon } from '../__styles__'

describe('PersistantNotification component', () => {
it('should match a snapshot', () => {
const wrapper = shallow(<PersistantNotification>I am notification</PersistantNotification>)
expect(wrapper).toMatchSnapshot()
})

it('should match a snapshot when given an intent', () => {
const wrapper = shallow(<PersistantNotification intent="critical">I am notification</PersistantNotification>)
expect(wrapper).toMatchSnapshot()
})

it('should match a snapshot when expanded', () => {
const wrapper = shallow(<PersistantNotification isExpanded={true}>I am notification</PersistantNotification>)
expect(wrapper).toMatchSnapshot()
})

it('should fire the onStepClick event correctly', () => {
const spy = jest.fn()
const wrapper = shallow(
<PersistantNotification intent="critical" onExpansionToggle={spy}>
I am notification
</PersistantNotification>,
)
wrapper.find(`.${elPnIcon}`).first().simulate('click')
expect(spy).toHaveBeenCalledTimes(1)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { cx } from 'linaria'
import * as React from 'react'
import { ElPersistantNotification, elPnIcon, elPnContent } from './__styles__'
import { Icon, IconNames } from '../Icon'
import { elIsActive } from '../../styles-v3/base/states'
import { Intent, getIntentClassName } from '../../helpers/v3/intent'

export interface IPersistantNotification extends React.HTMLAttributes<HTMLDivElement> {
icon?: IconNames
intent?: Intent
className?: string
isExpanded?: boolean
onExpansionToggle?: (newState: boolean) => void
}

export const PersistantNotification: React.FC<IPersistantNotification> = ({
icon = 'info',
intent = 'secondary',
className,
isExpanded = false,
onExpansionToggle,
children,
...rest
}) => {
const intentClassName = getIntentClassName(intent)
const combinedClassName = cx(className, intentClassName, isExpanded && elIsActive)

return (
<ElPersistantNotification className={combinedClassName} {...rest}>
<div className={elPnIcon} onClick={() => onExpansionToggle && onExpansionToggle(!isExpanded)}>
<Icon icon={icon} />
</div>
<div className={elPnContent}>{children}</div>
</ElPersistantNotification>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { Meta, Story, Canvas, ArgsTable } from '@storybook/addon-docs/blocks'
import { useState } from 'react'
import { PersistantNotification } from './index'

<Meta title="V3/PersistantNotification" component={PersistantNotification} />

# PersistantNotification

Underlying tag: `<div>`

## Full React example using `useState` and the default CSS position: fixed

<Canvas>
<Story name="Full React example (fixed position)">
{() => {
const [isExpanded, setIsExpanded] = useState(false)
return (
<>
The element uses CSS position fixed by default, which means the component "floats" on top of all other
content. This example can be seen in the top right corner of the screen. Only one PersistantNotification per
page is supported.
<PersistantNotification isExpanded={isExpanded} onExpansionToggle={setIsExpanded}>
Here is a persistant notification. Here is more content. Here is more content. Here is more content. Here is
more content. Here is more content.
</PersistantNotification>
</>
)
}}
</Story>
</Canvas>

<ArgsTable of={PersistantNotification} />

## Inline examples

These stories have been given a custom CSS positon (`position: relative`) so that they appear inline. This makes it easier to preview these examples, but in normal usage they will pin to the top right of the screen as in the above example.

### Default usage

<Canvas>
<Story name="Default Usage">
<PersistantNotification isExpanded={true} style={{ position: 'relative' }}>
Here is a persistant notification
</PersistantNotification>
</Story>
</Canvas>

### With longer content

The notification is set to a max-width of 50% of the parent element (in normal usage this is the entire window). If the content is longer the box just grows downwards.

<Canvas>
<Story name="Long text">
<PersistantNotification isExpanded={true} style={{ position: 'relative' }}>
Here is a persistant notification. Here is more content. Here is more content. Here is more content. Here is more
content. Here is more content.
</PersistantNotification>
</Story>
</Canvas>

### With a different icon

<Canvas>
<Story name="With a different icon">
<PersistantNotification isExpanded={true} icon="warning" style={{ position: 'relative' }}>
Here's some infomation about the thing you should be warned about, so bad things don't happen.
</PersistantNotification>
</Story>
</Canvas>

### With different intents

<Canvas>
<Story name="Intent: primary">
<PersistantNotification isExpanded={true} intent="primary" style={{ position: 'relative' }}>
Here's a notification with a primary intent.
</PersistantNotification>
</Story>
<Story name="Intent: secondary (default)">
<PersistantNotification isExpanded={true} intent="secondary" style={{ position: 'relative' }}>
Here's a notification with a secondary intent.
</PersistantNotification>
</Story>
<Story name="Intent: critical">
<PersistantNotification isExpanded={true} intent="critical" style={{ position: 'relative' }}>
Here's a notification with a critical intent.
</PersistantNotification>
</Story>
<Story name="Intent: success">
<PersistantNotification isExpanded={true} intent="success" style={{ position: 'relative' }}>
Here's a notification with a success intent.
</PersistantNotification>
</Story>
<Story name="Intent: danger">
<PersistantNotification isExpanded={true} intent="danger" style={{ position: 'relative' }}>
Here's a notification with a danger intent.
</PersistantNotification>
</Story>
</Canvas>

### Collapsed/Expanded state

Set the prop `isExpanded` to expand/collapse the component. In the collapsed state the whole component is moved to the right. In normal usage when the component is fixed to the right of the screen this means the content is out of view.

## Other attributes

All other standard HTML attributes for `<div>` are supported and are passed through to the React component.
2 changes: 1 addition & 1 deletion packages/elements/src/styles-v3/base/variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const elVariables = css`
--intent-secondary-dark-text: var(--color-white);
--intent-critical-dark-text: var(--color-white);
--intent-success-dark-text: var(--color-white);
--intent-danger-light-text: var(--color-white);
--intent-danger-dark-text: var(--color-white);
/** font variables */
--font-sans-serif: 'PT Sans', Helvetica, Arial, sans-serif;
Expand Down

0 comments on commit 65f139e

Please sign in to comment.