Skip to content

Commit 6827327

Browse files
committed
feat(WebexMemberRoster): split component into members roster and meeting roster
1 parent d1edde8 commit 6827327

File tree

10 files changed

+686
-512
lines changed

10 files changed

+686
-512
lines changed

.storybook/preview.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,14 @@ export const parameters = {
4141
'Platform',
4242
[
4343
'Webex Avatar',
44-
'Webex Member Roster',
4544
],
4645
'Messaging',
4746
[
4847
'Webex Messaging',
4948
'Webex Activity Stream',
5049
'Webex Activity',
5150
'Webex Member',
51+
'Webex Member Roster',
5252
],
5353
'Meetings',
5454
[
@@ -60,6 +60,7 @@ export const parameters = {
6060
'Webex Remote Media',
6161
'Webex Meeting Control',
6262
'Webex Meeting Participant',
63+
'Webex Meeting Roster',
6364
],
6465
],
6566
},
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import {DestinationType} from '@webex/component-adapter-interfaces';
4+
import webexComponentClasses from '../helpers';
5+
6+
import Button from '../generic/Button/Button';
7+
import Icon from '../generic/Icon/Icon';
8+
import Title from '../generic/Title/Title';
9+
import useMembers from '../hooks/useMembers';
10+
import {useMe} from '../hooks';
11+
import WebexMeetingParticipant from '../WebexMeetingParticipant/WebexMeetingParticipant';
12+
13+
// TODO: Figure out how to import JS Doc definitions and remove duplication.
14+
/**
15+
* Enum for types of destinations.
16+
*
17+
* @external DestinationType
18+
* @see {@link https://github.com/webex/component-adapter-interfaces/blob/master/src/MembershipsAdapter.js#L21}
19+
*/
20+
21+
/**
22+
* Displays the roster of Webex meeting.
23+
*
24+
* @param {object} props Data passed to the component
25+
* @param {string} props.className Custom CSS class to apply
26+
* @param {string} props.meetingID ID of the meeting for which to get members
27+
* @param {object} props.style Custom style to apply
28+
* @param {Function} props.onClose Action to close the roster
29+
* @returns {object} JSX of the component
30+
*
31+
*/
32+
export default function WebexMeetingRoster({
33+
className,
34+
meetingID,
35+
style,
36+
onClose,
37+
}) {
38+
const members = useMembers(meetingID, DestinationType.MEETING);
39+
const {orgID} = useMe();
40+
41+
const [cssClasses, sc] = webexComponentClasses('meeting-roster', className);
42+
43+
const renderMembers = (data) => data.map(
44+
({ID}) => (
45+
<WebexMeetingParticipant
46+
meetingID={meetingID}
47+
personID={ID}
48+
key={ID}
49+
/>
50+
),
51+
);
52+
53+
const renderSection = (data, title) => data.length > 0 && (
54+
<>
55+
<h5 className={sc('section-title')}>{title}</h5>
56+
{renderMembers(data)}
57+
</>
58+
);
59+
60+
const warningExternalMembers = members.some(
61+
(member) => member.orgID !== undefined && orgID !== undefined && member.orgID !== orgID,
62+
) && (
63+
<div className={sc('external-user-warning')}>
64+
<Icon name="external-user" size={20} className={sc('external-user-icon')} />
65+
<div className={sc('external-user-message')}>People outside your company are included in this space</div>
66+
</div>
67+
);
68+
69+
return (
70+
<div className={cssClasses} style={style}>
71+
<div className={sc('header')}>
72+
<Title type="section" className={sc('title')}>
73+
Participants (
74+
{members ? members.length : <i>loading...</i>}
75+
)
76+
</Title>
77+
<Button
78+
type="ghost"
79+
size={28}
80+
onClick={onClose}
81+
tabIndex={50}
82+
ariaLabel="Close participants panel"
83+
>
84+
<Icon name="cancel" size={16} />
85+
</Button>
86+
</div>
87+
{warningExternalMembers}
88+
<div className={sc('members')}>
89+
{renderSection(members.filter((member) => member.inMeeting), 'In the meeting')}
90+
{renderSection(members.filter((member) => !member.inMeeting), 'Not in the meeting')}
91+
</div>
92+
</div>
93+
);
94+
}
95+
96+
WebexMeetingRoster.propTypes = {
97+
className: PropTypes.string,
98+
meetingID: PropTypes.string.isRequired,
99+
style: PropTypes.shape(),
100+
onClose: PropTypes.func,
101+
};
102+
103+
WebexMeetingRoster.defaultProps = {
104+
className: '',
105+
style: undefined,
106+
onClose: undefined,
107+
};
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
$C: #{$WEBEX_COMPONENTS_CLASS_PREFIX}-meeting-roster;
2+
3+
.#{$C} {
4+
display: flex;
5+
flex-direction: column;
6+
7+
color: var(--wxc-text-color);
8+
background: var(--wxc-secondary-background);
9+
border-radius: 0.5rem 0 0 0;
10+
padding: 0.625rem 0 0.625rem 0.75rem;
11+
12+
13+
.#{$C}__section-title {
14+
color: var(--wxc-secondary-text-color);
15+
font-size: 0.875rem;
16+
line-height: 1.375rem;
17+
margin: 0;
18+
letter-spacing: normal;
19+
padding: 0.438rem 0 0.188rem 0;
20+
}
21+
22+
.#{$C}__external-user-warning {
23+
color: var(--wxc-warning-color);
24+
display: flex;
25+
align-items: flex-start;
26+
font-size: 0.875rem;
27+
line-height: 1.375rem;
28+
margin-top: 0.75rem;
29+
30+
.#{$C}__external-user-icon {
31+
margin-right: 0.578rem;
32+
}
33+
34+
.#{$C}__external-user-message {
35+
flex: 1;
36+
}
37+
}
38+
39+
.#{$C}__header {
40+
display: flex;
41+
align-items: center;
42+
padding-right: 0.375rem;
43+
44+
.#{$C}__title {
45+
font-family: $brand-font-bold;
46+
margin: 0;
47+
flex: 1;
48+
}
49+
}
50+
51+
.#{$C}__members {
52+
overflow-y: auto;
53+
flex: 1;
54+
padding-right: 1.25rem;
55+
scrollbar-width: thin; //works only for Firefox
56+
}
57+
58+
::-webkit-scrollbar {
59+
width: 1rem;
60+
}
61+
62+
::-webkit-scrollbar-thumb {
63+
background: var(--wxc-scrollbar--thumb);
64+
border-radius: 1rem;
65+
}
66+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import React from 'react';
2+
import WebexMeetingRoster from './WebexMeetingRoster';
3+
4+
export default {
5+
title: 'Meetings/Webex Meeting Roster',
6+
component: WebexMeetingRoster,
7+
};
8+
9+
const Template = (args) => {
10+
const style = {
11+
width: '23.25rem',
12+
maxHeight: '100%',
13+
...args.style,
14+
};
15+
const props = {...args, style};
16+
17+
return <WebexMeetingRoster {...props} />;
18+
};
19+
20+
export const Meeting = Template.bind({});
21+
Meeting.args = {
22+
meetingID: 'meeting2',
23+
};

0 commit comments

Comments
 (0)