Skip to content

Commit 90c7c24

Browse files
committed
feat(ActivityStream): implement empty state
1 parent 48fbeb9 commit 90c7c24

File tree

12 files changed

+557
-2
lines changed

12 files changed

+557
-2
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Webex Activity Stream Component
2+
3+
Webex activity stream component displays a list of activities of a room.
4+
5+
<p align="center">
6+
<span>picture coming up</span>
7+
</p>
8+
9+
## Preview
10+
11+
To see all the different possible states of the Webex Activity Stream component, you can run our Storybook:
12+
13+
```shell
14+
npm start
15+
```
16+
17+
## Embed
18+
19+
1. Create a component adapter from which the data will be retrieved (See [adapters](../../adapters)). For instance:
20+
21+
```js
22+
const jsonAdapter = new RoomsJSONAdapter(rooms);
23+
```
24+
25+
2. Create a component instance by passing the room ID as a string and the [component data adapter](../../adapters/RoomsAdapter.js) that we created previously
26+
27+
```js
28+
<WebexActivityStream roomID="roomID" adapter={jsonAdapter} />
29+
```
30+
31+
The component knows how to manage its data. If anything changes in the data source that the adapter manages, the component will also update on its own.
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
import {RoomType} from '../../adapters/RoomsAdapter';
5+
import './WebexActivityStream.scss';
6+
import {useRoom, useActivityStream} from '../hooks';
7+
8+
export function GreetingDirectSVG() {
9+
return (
10+
<svg
11+
width="61px"
12+
height="60px"
13+
viewBox="0 0 61 60"
14+
version="1.1"
15+
xlns="http://www.w3.org/2000/svg"
16+
xlinkHref="http://www.w3.org/1999/xlink"
17+
>
18+
<g id="Page-1" stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
19+
<g id="one-on-one" stroke="#343537" strokeWidth="1.12800004">
20+
<g id="Group-39" transform="translate(9.056604, 6.895610)">
21+
<path
22+
d="M42.2831173,43.3165345 L42.1768007,41.8220793 C41.3164233,36.6788756 36.8550615,32.9083712 31.6258573,32.9083712 L10.9787695,32.9083712 C5.75153404,32.9083712 1.28820345,36.6788756 0.429794914,41.8220793 L0.323478261,43.3126069"
23+
id="Stroke-15"
24+
strokeLinecap="round"
25+
strokeLinejoin="round"
26+
/>
27+
<path
28+
d="M28.8517801,32.9472545 C31.9664643,32.2991991 35.0476784,31.2426723 38.0265135,29.7580362 L38.0265135,16.8362033 C38.0265135,7.54740855 30.4780312,0.0162187139 21.1635111,0.0162187139 C11.8509598,0.0162187139 4.30050861,7.54740855 4.30050861,16.8362033 L4.30050861,29.7580362 C6.81470057,31.0089796 10.4452174,32.279561 13.1700738,32.9472545"
29+
id="Stroke-27"
30+
/>
31+
<path
32+
d="M9.72108285,12.4414055 C11.4733388,13.1660493 13.0543068,13.7257335 14.9168171,14.1165306 L16.5942576,9.9945052 L16.6926989,14.6506854 C21.4907301,15.08665 28.1197703,14.2716712 32.8784249,12.305903 L32.9335521,12.305903 C34.1995078,14.36397 34.9279737,16.783377 34.9279737,19.373635 C34.9279737,26.8498383 28.8521739,32.9101386 21.3568499,32.9101386 C13.8615258,32.9101386 7.78375718,26.8498383 7.78375718,19.373635 C7.78375718,16.9856488 8.40393765,14.7429842 9.49269893,12.7948903 L9.72108285,12.4414055 Z"
33+
id="Stroke-29"
34+
strokeLinecap="round"
35+
strokeLinejoin="round"
36+
/>
37+
</g>
38+
<path
39+
d="M59.5436259,30.0538891 C59.5436259,13.8348286 46.360361,0.685194455 30.0998195,0.685194455 C23.531813,0.685194455 17.4855455,2.85716211 12.589073,6.48430882 L8.65338802,9.97398922 C3.70769483,15.227166 0.656013126,22.2772237 0.656013126,30.0538891 C0.656013126,46.2729496 13.8392781,59.4206199 30.0998195,59.4206199 C38.5086792,59.4206199 46.0965381,55.9054101 51.4615915,50.2653639 C56.4919442,44.9493454 59.5436259,37.8560839 59.5436259,30.0538891 Z"
40+
id="Stroke-31"
41+
strokeLinecap="round"
42+
strokeLinejoin="round"
43+
/>
44+
</g>
45+
</g>
46+
</svg>
47+
);
48+
}
49+
50+
export function GreetingSpaceSVG() {
51+
return (
52+
<svg
53+
width="61px"
54+
height="61px"
55+
viewBox="0 0 61 61"
56+
version="1.1"
57+
xmlns="http://www.w3.org/2000/svg"
58+
xlinkHref="http://www.w3.org/1999/xlink"
59+
>
60+
<g id="Page-1" stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
61+
<g id="space" transform="translate(-14.000000, -3.000000)">
62+
<rect id="Rectangle-2" x="0" y="0" width="90" height="68" />
63+
<g id="Group" transform="translate(15.000000, 4.000000)" stroke="#343537">
64+
<path
65+
d="M1.77076923,19.42 C0.624615385,22.5692308 0,25.9676923 0,29.5123077 C0,45.8107692 13.2123077,59.0246154 29.5123077,59.0246154 C45.8107692,59.0246154 59.0246154,45.8107692 59.0246154,29.5123077 C59.0246154,13.2138462 45.8107692,0 29.5123077,0 C20.6169231,0 12.6415385,3.93538462 7.23076923,10.16"
66+
id="Stroke-9"
67+
/>
68+
<path
69+
d="M56.9563077,23.8953846 C56.9563077,17.3984615 51.6901538,12.1323077 45.1932308,12.1323077 C38.6947692,12.1323077 33.4286154,17.3984615 33.4286154,23.8953846 C33.4286154,30.3923077 38.6947692,35.6584615 45.1932308,35.6584615 C51.6901538,35.6584615 56.9563077,30.3923077 56.9563077,23.8953846 Z"
70+
id="Stroke-11"
71+
strokeLinecap="round"
72+
strokeLinejoin="round"
73+
/>
74+
<path
75+
d="M32.0841538,40.082 C33.3472308,37.1358462 36.0087692,35.6589231 38.7826154,35.6589231 L51.5703077,35.6589231 C53.9856923,35.6589231 56.1887692,36.8435385 57.5349231,38.7373846"
76+
id="Stroke-13"
77+
strokeLinecap="round"
78+
/>
79+
<path
80+
d="M33.7678462,58.6032308 L32.8047692,42.5016923 C32.6586154,41.6278462 32.3832308,40.8032308 31.9986154,40.0463077"
81+
id="Stroke-15"
82+
strokeLinecap="round"
83+
/>
84+
<path
85+
d="M31.9986154,40.0464615 C30.5786154,37.2495385 27.6801538,35.3849231 24.4016923,35.3849231 L7.95707692,35.3849231 C5.39246154,35.3849231 3.06015385,36.5264615 1.48630769,38.3618462"
86+
id="Stroke-17"
87+
strokeLinecap="round"
88+
/>
89+
<path
90+
d="M22.5238462,35.4156923 C25.1838462,34.8603077 27.8161538,33.9572308 30.3623077,32.6849231 L30.3623077,21.6187692 C30.3623077,13.6618462 23.9115385,7.21261538 15.9561538,7.21261538 C8.00076923,7.21261538 1.55153846,13.6618462 1.55153846,21.6187692 L1.55153846,32.6849231 C3.69769231,33.7556923 6.80076923,34.8433846 9.12692308,35.4156923"
91+
id="Stroke-19"
92+
/>
93+
<path
94+
d="M6.18107692,17.8535385 C7.678,18.4735385 9.02876923,18.952 10.6195385,19.2889231 L12.0533846,15.7581538 L12.138,19.7458462 C16.2349231,20.1181538 21.898,19.4212308 25.9641538,17.7381538 L26.0103077,17.7381538 C27.0918462,19.4996923 27.7149231,21.572 27.7149231,23.7904615 C27.7149231,30.1935385 22.5241538,35.3843077 16.1210769,35.3843077 C9.718,35.3843077 4.52723077,30.1935385 4.52723077,23.7904615 C4.52723077,21.7458462 5.05646154,19.8243077 5.98569231,18.1566154 L6.18107692,17.8535385 Z"
95+
id="Stroke-21"
96+
strokeLinecap="round"
97+
strokeLinejoin="round"
98+
/>
99+
<path
100+
d="M0.000153846167,29.5121538 C0.000153846167,45.8106154 13.2124615,59.0244615 29.5124615,59.0244615 C45.8109231,59.0244615 59.0247692,45.8106154 59.0247692,29.5121538"
101+
id="Stroke-23"
102+
/>
103+
<path
104+
d="M39.0553846,14.1330769 C42.1815385,18.3853846 47.8369231,20.27 53.0507692,18.3823077 C54.4338462,17.8823077 55.6723077,17.1561538 56.7369231,16.2623077"
105+
id="Stroke-25"
106+
strokeLinecap="round"
107+
strokeLinejoin="round"
108+
/>
109+
</g>
110+
</g>
111+
</g>
112+
</svg>
113+
);
114+
}
115+
116+
export function Greeting(props) {
117+
let svg = <GreetingSpaceSVG />;
118+
let description = `This is a shared space between you and other group members. Here's where you'll see shared messages, files, and a call history with this space.`;
119+
120+
if (props.personName) {
121+
svg = <GreetingDirectSVG />;
122+
description = `This is your private conversation with ${props.personName}. Here's where you'll see shared messages, files, and a call history with this person.`;
123+
}
124+
125+
return (
126+
<div className="greeting">
127+
<div className="greeting-header">
128+
{svg}
129+
<div className="greeting-description">{description}</div>
130+
</div>
131+
</div>
132+
);
133+
}
134+
135+
Greeting.propTypes = {
136+
personName: PropTypes.string.isRequired,
137+
};
138+
139+
export default function WebexActivityStream(props) {
140+
const {roomID, adapter} = props;
141+
const {title, roomType} = useRoom(roomID, adapter);
142+
const activityIDs = useActivityStream(roomID, adapter);
143+
const personName = roomType === RoomType.DIRECT ? title : '';
144+
145+
return <div className="activity-stream">{!activityIDs.length && <Greeting personName={personName} />}</div>;
146+
}
147+
148+
WebexActivityStream.propTypes = {
149+
roomID: PropTypes.string.isRequired,
150+
adapter: PropTypes.object.isRequired,
151+
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
.greeting {
2+
display: block;
3+
flex-direction: column;
4+
justify-content: center;
5+
align-items: center;
6+
max-width: 37.5rem;
7+
text-align: center;
8+
color: $gray-dark-3;
9+
10+
.greeting-header {
11+
@include header-fonts;
12+
}
13+
14+
.greeting-description {
15+
@include body-fonts;
16+
padding: 0 3.125rem;
17+
}
18+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from 'react';
2+
import {storiesOf} from '@storybook/react';
3+
4+
import RoomsJSONAdapter from '../../adapters/RoomsJSONAdapter';
5+
import {RoomType} from '../../adapters/RoomsAdapter';
6+
import rooms from '../../data/rooms';
7+
8+
import WebexActivityStream from './WebexActivityStream';
9+
10+
// Setup for the stories
11+
const [roomID] = Object.keys(rooms);
12+
const stories = storiesOf('Webex Activity Stream', module);
13+
const newRooms = {};
14+
15+
// Stories
16+
stories.add('empty group stream', () => <WebexActivityStream roomID={roomID} adapter={new RoomsJSONAdapter(rooms)} />);
17+
stories.add('empty 1:1 stream', () => {
18+
newRooms[roomID] = {
19+
...rooms[roomID],
20+
roomType: RoomType.DIRECT,
21+
};
22+
23+
return <WebexActivityStream roomID={roomID} adapter={new RoomsJSONAdapter(newRooms)} />;
24+
});
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import React from 'react';
2+
3+
import RoomsJSONAdapter from '../../adapters/RoomsJSONAdapter';
4+
import {RoomType} from '../../adapters/RoomsAdapter';
5+
import rooms from '../../data/rooms';
6+
7+
import WebexActivityStream, {Greeting, GreetingSpaceSVG, GreetingDirectSVG} from './WebexActivityStream';
8+
9+
jest.mock('../hooks/useRoom');
10+
jest.mock('../hooks/useActivityStream');
11+
12+
describe('Webex Activity Stream component', () => {
13+
let roomID, newRooms, roomsAdapter;
14+
15+
beforeEach(() => {
16+
[roomID] = Object.keys(rooms);
17+
newRooms = rooms;
18+
roomsAdapter = new RoomsJSONAdapter(rooms);
19+
});
20+
21+
describe('Greeting Space SVG snapshot', () => {
22+
test('matches with greeting space SVG', () => {
23+
expect(shallow(<GreetingSpaceSVG />)).toMatchSnapshot();
24+
});
25+
});
26+
27+
describe('Greeting 1:1 SVG snapshot', () => {
28+
test('matches with greeting 1:1 SVG', () => {
29+
expect(shallow(<GreetingDirectSVG />)).toMatchSnapshot();
30+
});
31+
});
32+
33+
describe('Greeting component snapshot', () => {
34+
test('matches with empty space', () => {
35+
expect(shallow(<Greeting personName="" />)).toMatchSnapshot();
36+
});
37+
38+
test('matches with empty 1:1', () => {
39+
expect(shallow(<Greeting personName="personName" />)).toMatchSnapshot();
40+
});
41+
});
42+
43+
describe('Webex Activity Stream snapshots', () => {
44+
test('matches with empty group stream', () => {
45+
expect(shallow(<WebexActivityStream roomID={roomID} adapter={roomsAdapter} />)).toMatchSnapshot();
46+
});
47+
48+
test('matches with empty direct stream', () => {
49+
newRooms[roomID] = {
50+
...rooms[roomID],
51+
roomType: RoomType.DIRECT,
52+
};
53+
roomsAdapter = new RoomsJSONAdapter(newRooms);
54+
55+
expect(shallow(<WebexActivityStream roomID={roomID} adapter={roomsAdapter} />)).toMatchSnapshot();
56+
});
57+
});
58+
59+
afterEach(() => {
60+
roomsAdapter = null;
61+
newRooms = null;
62+
roomID = null;
63+
});
64+
});

0 commit comments

Comments
 (0)