From 68273272c6922f67ee4af76768c13c050f9863f3 Mon Sep 17 00:00:00 2001 From: Benjamin Kinga Date: Wed, 30 Mar 2022 13:31:28 -0500 Subject: [PATCH] feat(WebexMemberRoster): split component into members roster and meeting roster --- .storybook/preview.js | 3 +- .../WebexMeetingRoster/WebexMeetingRoster.jsx | 107 ++++ .../WebexMeetingRoster.scss | 66 +++ .../WebexMeetingRoster.stories.js | 23 + .../WebexMeetingRoster.stories.storyshot | 475 +++++++++++++++++ .../WebexMemberRoster/WebexMemberRoster.jsx | 30 +- .../WebexMemberRoster.stories.js | 14 +- .../WebexMemberRoster.stories.storyshot | 478 +----------------- src/components/_components.scss | 1 + src/components/index.js | 1 + 10 files changed, 686 insertions(+), 512 deletions(-) create mode 100644 src/components/WebexMeetingRoster/WebexMeetingRoster.jsx create mode 100644 src/components/WebexMeetingRoster/WebexMeetingRoster.scss create mode 100644 src/components/WebexMeetingRoster/WebexMeetingRoster.stories.js create mode 100644 src/components/WebexMeetingRoster/__snapshots__/WebexMeetingRoster.stories.storyshot diff --git a/.storybook/preview.js b/.storybook/preview.js index b03539917..b8b1368d8 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -41,7 +41,6 @@ export const parameters = { 'Platform', [ 'Webex Avatar', - 'Webex Member Roster', ], 'Messaging', [ @@ -49,6 +48,7 @@ export const parameters = { 'Webex Activity Stream', 'Webex Activity', 'Webex Member', + 'Webex Member Roster', ], 'Meetings', [ @@ -60,6 +60,7 @@ export const parameters = { 'Webex Remote Media', 'Webex Meeting Control', 'Webex Meeting Participant', + 'Webex Meeting Roster', ], ], }, diff --git a/src/components/WebexMeetingRoster/WebexMeetingRoster.jsx b/src/components/WebexMeetingRoster/WebexMeetingRoster.jsx new file mode 100644 index 000000000..827f46d4e --- /dev/null +++ b/src/components/WebexMeetingRoster/WebexMeetingRoster.jsx @@ -0,0 +1,107 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import {DestinationType} from '@webex/component-adapter-interfaces'; +import webexComponentClasses from '../helpers'; + +import Button from '../generic/Button/Button'; +import Icon from '../generic/Icon/Icon'; +import Title from '../generic/Title/Title'; +import useMembers from '../hooks/useMembers'; +import {useMe} from '../hooks'; +import WebexMeetingParticipant from '../WebexMeetingParticipant/WebexMeetingParticipant'; + +// TODO: Figure out how to import JS Doc definitions and remove duplication. +/** + * Enum for types of destinations. + * + * @external DestinationType + * @see {@link https://github.com/webex/component-adapter-interfaces/blob/master/src/MembershipsAdapter.js#L21} + */ + +/** + * Displays the roster of Webex meeting. + * + * @param {object} props Data passed to the component + * @param {string} props.className Custom CSS class to apply + * @param {string} props.meetingID ID of the meeting for which to get members + * @param {object} props.style Custom style to apply + * @param {Function} props.onClose Action to close the roster + * @returns {object} JSX of the component + * + */ +export default function WebexMeetingRoster({ + className, + meetingID, + style, + onClose, +}) { + const members = useMembers(meetingID, DestinationType.MEETING); + const {orgID} = useMe(); + + const [cssClasses, sc] = webexComponentClasses('meeting-roster', className); + + const renderMembers = (data) => data.map( + ({ID}) => ( + + ), + ); + + const renderSection = (data, title) => data.length > 0 && ( + <> +
{title}
+ {renderMembers(data)} + + ); + + const warningExternalMembers = members.some( + (member) => member.orgID !== undefined && orgID !== undefined && member.orgID !== orgID, + ) && ( +
+ +
People outside your company are included in this space
+
+ ); + + return ( +
+
+ + Participants ( + {members ? members.length : <i>loading...</i>} + ) + + +
+ {warningExternalMembers} +
+ {renderSection(members.filter((member) => member.inMeeting), 'In the meeting')} + {renderSection(members.filter((member) => !member.inMeeting), 'Not in the meeting')} +
+
+ ); +} + +WebexMeetingRoster.propTypes = { + className: PropTypes.string, + meetingID: PropTypes.string.isRequired, + style: PropTypes.shape(), + onClose: PropTypes.func, +}; + +WebexMeetingRoster.defaultProps = { + className: '', + style: undefined, + onClose: undefined, +}; diff --git a/src/components/WebexMeetingRoster/WebexMeetingRoster.scss b/src/components/WebexMeetingRoster/WebexMeetingRoster.scss new file mode 100644 index 000000000..ba2cd08c3 --- /dev/null +++ b/src/components/WebexMeetingRoster/WebexMeetingRoster.scss @@ -0,0 +1,66 @@ +$C: #{$WEBEX_COMPONENTS_CLASS_PREFIX}-meeting-roster; + +.#{$C} { + display: flex; + flex-direction: column; + + color: var(--wxc-text-color); + background: var(--wxc-secondary-background); + border-radius: 0.5rem 0 0 0; + padding: 0.625rem 0 0.625rem 0.75rem; + + + .#{$C}__section-title { + color: var(--wxc-secondary-text-color); + font-size: 0.875rem; + line-height: 1.375rem; + margin: 0; + letter-spacing: normal; + padding: 0.438rem 0 0.188rem 0; + } + + .#{$C}__external-user-warning { + color: var(--wxc-warning-color); + display: flex; + align-items: flex-start; + font-size: 0.875rem; + line-height: 1.375rem; + margin-top: 0.75rem; + + .#{$C}__external-user-icon { + margin-right: 0.578rem; + } + + .#{$C}__external-user-message { + flex: 1; + } + } + + .#{$C}__header { + display: flex; + align-items: center; + padding-right: 0.375rem; + + .#{$C}__title { + font-family: $brand-font-bold; + margin: 0; + flex: 1; + } + } + + .#{$C}__members { + overflow-y: auto; + flex: 1; + padding-right: 1.25rem; + scrollbar-width: thin; //works only for Firefox + } + + ::-webkit-scrollbar { + width: 1rem; + } + + ::-webkit-scrollbar-thumb { + background: var(--wxc-scrollbar--thumb); + border-radius: 1rem; + } +} diff --git a/src/components/WebexMeetingRoster/WebexMeetingRoster.stories.js b/src/components/WebexMeetingRoster/WebexMeetingRoster.stories.js new file mode 100644 index 000000000..e72f3851d --- /dev/null +++ b/src/components/WebexMeetingRoster/WebexMeetingRoster.stories.js @@ -0,0 +1,23 @@ +import React from 'react'; +import WebexMeetingRoster from './WebexMeetingRoster'; + +export default { + title: 'Meetings/Webex Meeting Roster', + component: WebexMeetingRoster, +}; + +const Template = (args) => { + const style = { + width: '23.25rem', + maxHeight: '100%', + ...args.style, + }; + const props = {...args, style}; + + return ; +}; + +export const Meeting = Template.bind({}); +Meeting.args = { + meetingID: 'meeting2', +}; diff --git a/src/components/WebexMeetingRoster/__snapshots__/WebexMeetingRoster.stories.storyshot b/src/components/WebexMeetingRoster/__snapshots__/WebexMeetingRoster.stories.storyshot new file mode 100644 index 000000000..7cdebf408 --- /dev/null +++ b/src/components/WebexMeetingRoster/__snapshots__/WebexMeetingRoster.stories.storyshot @@ -0,0 +1,475 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Storyshots Meetings/Webex Meeting Roster Meeting 1`] = ` +Array [ +
+
+
+ Participants ( + 6 + ) +
+ + +
+
+ + + + + + +
+ People outside your company are included in this space +
+
+
+
+ In the meeting +
+
+
+
+ + + BG + + + avatar +
+
+
+
+ Barbara German +
+
+ You +
+
+ + + + +
+
+
+
+ + + GE + + + avatar +
+
+
+
+ Giacomo Edwards +
+
+ + + + + +
+
+
+
+ + + BS + + + avatar +
+
+
+
+ Brenda Song +
+
+ Presenter +
+
+ + + + + + + + + +
+
+
+
+ + + SD + + + avatar +
+
+
+
+ Simon Damiano +
+
+ Host +
+
+ + + + +
+
+
+
+ + + BS + + + avatar +
+
+
+
+ Brandon Seeger + + (Guest) + +
+
+ Gmail.com +
+
+
+
+ Not in the meeting +
+
+
+
+ + + MR + + + avatar +
+
+
+
+ Maria Rossi + + (Guest) + +
+
+ Gmail.com +
+
+
+
+
, +