Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"no-restricted-imports": 0,
"class-methods-use-this": 0,
"no-use-before-define": 0,
"object-curly-newline": 0,
"react/destructuring-assignment": 0,
"react/no-access-state-in-setstate": "error",
"react/jsx-filename-extension": 0,
Expand Down
1 change: 1 addition & 0 deletions config/styleguide.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"description": "React components",
"components": [
"Avatar",
"AvatarStack",
"BannerNotification",
"BannerNotifications",
"Button",
Expand Down
7 changes: 1 addition & 6 deletions source/components/Avatar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,11 @@ import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import AvatarImage from './AvatarImage';
import Badge from './Badge';
import { getAvatarImage } from './utils';

import './styles.scss';

const getAvatarImage = (href, alt, src) => {
const avatarImage = <AvatarImage alt={alt} src={src} />;
return href ? <a href={href}>{avatarImage}</a> : avatarImage;
};

const Avatar = ({
alt,
badge,
Expand Down
9 changes: 9 additions & 0 deletions source/components/Avatar/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';

import AvatarImage from './AvatarImage';

// eslint-disable-next-line
export function getAvatarImage(href, alt, src) {
const avatarImage = <AvatarImage alt={alt} src={src} />;
return href ? <a href={href}>{avatarImage}</a> : avatarImage;
}
35 changes: 35 additions & 0 deletions source/components/AvatarStack/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
Defaults:
```js
<AvatarStack
avatars={[{ src: 'https://static.wikia.nocookie.net/438f6e23-1a0f-4351-a34d-6f8fa734a246', alt: 'Image', link: 'some://valid.url', badge: 'admin' }, { src: 'https://static.wikia.nocookie.net/438f6e23-1a0f-4351-a34d-6f8fa734a246', alt: 'Image', link: 'some://valid.url', badge: 'staff' }, { src: 'https://static.wikia.nocookie.net/fcdbab4c-8838-487e-89fd-5f0782974edf', alt: 'Image', link: 'some://valid.url', badge: 'vstf' }]}
/>
```

With maxStackSize

```js
<AvatarStack
avatars={[{ src: 'https://static.wikia.nocookie.net/438f6e23-1a0f-4351-a34d-6f8fa734a246', alt: 'Image', link: 'some://valid.url', badge: 'admin' }, { src: 'https://static.wikia.nocookie.net/438f6e23-1a0f-4351-a34d-6f8fa734a246', alt: 'Image', link: 'some://valid.url', badge: 'staff' }, { src: 'https://static.wikia.nocookie.net/fcdbab4c-8838-487e-89fd-5f0782974edf', alt: 'Image', link: 'some://valid.url', badge: 'vstf' }]}
maxStackSize={2}
/>
```

With hidden overflow

```js
<AvatarStack
avatars={[{ src: 'https://static.wikia.nocookie.net/438f6e23-1a0f-4351-a34d-6f8fa734a246', alt: 'Image', link: 'some://valid.url', badge: 'admin' }, { src: 'https://static.wikia.nocookie.net/438f6e23-1a0f-4351-a34d-6f8fa734a246', alt: 'Image', link: 'some://valid.url', badge: 'staff' }, { src: 'https://static.wikia.nocookie.net/fcdbab4c-8838-487e-89fd-5f0782974edf', alt: 'Image', link: 'some://valid.url', badge: 'vstf' }]}
maxStackSize={2}
hideOverflow
/>
```

With overridden counter

```js
<AvatarStack
avatars={[{ src: 'https://static.wikia.nocookie.net/438f6e23-1a0f-4351-a34d-6f8fa734a246', alt: 'Image', link: 'some://valid.url', badge: 'admin' }, { src: 'https://static.wikia.nocookie.net/438f6e23-1a0f-4351-a34d-6f8fa734a246', alt: 'Image', link: 'some://valid.url', badge: 'staff' }, { src: 'https://static.wikia.nocookie.net/fcdbab4c-8838-487e-89fd-5f0782974edf', alt: 'Image', link: 'some://valid.url', badge: 'vstf' }]}
maxStackSize={2}
overrideCount={6}
/>
```
100 changes: 100 additions & 0 deletions source/components/AvatarStack/__snapshots__/index.spec.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`AvatarStack renders when there are less avatars than specified by maxStackSize 1`] = `
<div
className="wds-avatar-stack"
>
<Avatar
alt="User"
badge="admin"
href="some.link"
key="0"
src="some.url"
/>
<Avatar
alt="Other user"
badge=""
href="some.other.link"
key="1"
src="some.other.url"
/>
0
</div>
`;

exports[`AvatarStack renders when there are more avatars than specified by maxStackSize 1`] = `
<div
className="wds-avatar-stack"
>
<Avatar
alt="User"
badge="admin"
href="some.link"
key="0"
src="some.url"
/>
<div
className="wds-avatar-stack__overflow"
>
+1
</div>
</div>
`;

exports[`AvatarStack renders with default props 1`] = `
<div
className="wds-avatar-stack"
>
<Avatar
alt="User"
badge="admin"
href="some.link"
key="0"
src="some.url"
/>
<Avatar
alt="Other user"
badge=""
href="some.other.link"
key="1"
src="some.other.url"
/>
0
</div>
`;

exports[`AvatarStack renders with hideOverflow set to true 1`] = `
<div
className="wds-avatar-stack"
>
<Avatar
alt="User"
badge="admin"
href="some.link"
key="0"
src="some.url"
/>
</div>
`;

exports[`AvatarStack renders with overrideCount 1`] = `
<div
className="wds-avatar-stack"
>
<Avatar
alt="User"
badge="admin"
href="some.link"
key="0"
src="some.url"
/>
<Avatar
alt="Other user"
badge=""
href="some.other.link"
key="1"
src="some.other.url"
/>
0
</div>
`;
54 changes: 54 additions & 0 deletions source/components/AvatarStack/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import Avatar from '../Avatar';

import './styles.scss';

/* eslint-disable react/no-array-index-key */
const AvatarStack = ({ avatars, overrideCount, maxStackSize, hideOverflow, className }) => {
const count = overrideCount || avatars.length;
const overflow = !hideOverflow && (count > maxStackSize ? count - maxStackSize : 0);

return (
<div className={classNames('wds-avatar-stack', className)}>
{
avatars
.slice(0, maxStackSize)
.map(({ src, alt, link, badge = '' }, index) => (
<Avatar key={index} src={src} alt={alt} badge={badge} href={link} />
))
}
{
overflow && (
<div className="wds-avatar-stack__overflow">
{`+${overflow}`}
</div>
)
}
</div>
);
};

AvatarStack.propTypes = {
/* An array of `Avatar` props */
avatars: PropTypes.arrayOf(PropTypes.shape()).isRequired,
/* Additional class name */
className: PropTypes.string,
/* Flag to hide overflow */
hideOverflow: PropTypes.bool,
/* Max stack size */
maxStackSize: PropTypes.number,
/* If specified, it will be used instead of `avatars.length` */
overrideCount: PropTypes.number,
};

AvatarStack.defaultProps = {
className: '',
hideOverflow: false,
maxStackSize: 5,
overrideCount: 0,
};

export default AvatarStack;
48 changes: 48 additions & 0 deletions source/components/AvatarStack/index.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { shallow } from 'enzyme';
import React from 'react';
import merge from 'lodash/merge';

import AvatarStack from './index';

const defaultProps = {
avatars: [
{ src: 'some.url', alt: 'User', link: 'some.link', badge: 'admin' },
{ src: 'some.other.url', alt: 'Other user', link: 'some.other.link' },
],
};

function renderComponent(props) {
const computedProps = merge({}, defaultProps, props);

return shallow(<AvatarStack {...computedProps} />);
}

test('AvatarStack renders with default props', () => {
const wrapper = renderComponent();

expect(wrapper).toMatchSnapshot();
});

test('AvatarStack renders with overrideCount', () => {
const wrapper = renderComponent({ overrideCount: 1 });

expect(wrapper).toMatchSnapshot();
});

test('AvatarStack renders when there are less avatars than specified by maxStackSize', () => {
const wrapper = renderComponent({ maxStackSize: 3 });

expect(wrapper).toMatchSnapshot();
});

test('AvatarStack renders when there are more avatars than specified by maxStackSize', () => {
const wrapper = renderComponent({ maxStackSize: 1 });

expect(wrapper).toMatchSnapshot();
});

test('AvatarStack renders with hideOverflow set to true', () => {
const wrapper = renderComponent({ hideOverflow: true, maxStackSize: 1 });

expect(wrapper).toMatchSnapshot();
});
3 changes: 3 additions & 0 deletions source/components/AvatarStack/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@import "~design-system/dist/scss/wds-mixins/index.scss";
@import "~design-system/dist/scss/wds-variables/index.scss";
@import "~design-system/dist/scss/wds-components/_avatar-stack.scss";
2 changes: 1 addition & 1 deletion source/components/BannerNotification/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ But it can be rendered with close buttton:

Or with extra HTML:
```js
<BannerNotification type={'alert'}onClose={() => alert('Click')}>
<BannerNotification type={'alert'} onClose={() => alert('Click')}>
This is a text <a href="http://example.com">with a link</a>
</BannerNotification>
```
33 changes: 3 additions & 30 deletions source/components/BannerNotification/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,10 @@ import React from 'react';
import PropTypes from 'prop-types';

import IconCloseTiny from '../../icons/IconCloseTiny';
import IconAlertSmall from '../../icons/IconAlertSmall';
import IconCheckmarkSmall from '../../icons/IconCheckmarkSmall';
import IconErrorSmall from '../../icons/IconErrorSmall';
import IconFlagSmall from '../../icons/IconFlagSmall';

import './styles.scss';

function getIcon(type) {
switch (type) {
case ('alert'):
return <IconErrorSmall className="wds-banner-notification__icon-mark" />;
case ('warning'):
return <IconAlertSmall className="wds-banner-notification__icon-mark" />;
case ('success'):
return <IconCheckmarkSmall className="wds-banner-notification__icon-mark" />;
default:
return <IconFlagSmall className="wds-banner-notification__icon-mark" />;
}
}
import { getClassName, getIcon } from './utils';

function getClassName(type) {
switch (type) {
case ('alert'):
return 'wds-alert';
case ('warning'):
return 'wds-warning';
case ('success'):
return 'wds-success';
default:
return 'wds-message';
}
}
import './styles.scss';

/**
* This is a single component used in `BannerNotifications` component.
Expand All @@ -48,6 +20,7 @@ const BannerNotification = ({
</div>
);


BannerNotification.propTypes = {
/** Children to display */
children: PropTypes.node,
Expand Down
32 changes: 32 additions & 0 deletions source/components/BannerNotification/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';

import IconAlertSmall from '../../icons/IconAlertSmall';
import IconCheckmarkSmall from '../../icons/IconCheckmarkSmall';
import IconErrorSmall from '../../icons/IconErrorSmall';
import IconFlagSmall from '../../icons/IconFlagSmall';

export function getIcon(type) {
switch (type) {
case ('alert'):
return <IconErrorSmall className="wds-banner-notification__icon-mark" />;
case ('warning'):
return <IconAlertSmall className="wds-banner-notification__icon-mark" />;
case ('success'):
return <IconCheckmarkSmall className="wds-banner-notification__icon-mark" />;
default:
return <IconFlagSmall className="wds-banner-notification__icon-mark" />;
}
}

export function getClassName(type) {
switch (type) {
case ('alert'):
return 'wds-alert';
case ('warning'):
return 'wds-warning';
case ('success'):
return 'wds-success';
default:
return 'wds-message';
}
}
1 change: 1 addition & 0 deletions source/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export { default as FandomContentWell } from './FandomContentWell';
export { default as List } from './List';
// Other UI
export { default as Avatar } from './Avatar';
export { default as AvatarStack } from './AvatarStack';
export { default as ExpandableText } from './ExpandableText';
export { default as Switch } from './Switch';
export { default as Timeago } from './Timeago';
Expand Down