Skip to content

Commit

Permalink
feat(WebexMeetingControl): implement component
Browse files Browse the repository at this point in the history
Add sample meeting controls "join", "disabled-join" and "disabled-mute-audio" for
stories in Storybook.
  • Loading branch information
lalli-flores committed Dec 4, 2019
1 parent 5c89588 commit b4fcf5a
Show file tree
Hide file tree
Showing 12 changed files with 525 additions and 1 deletion.
86 changes: 85 additions & 1 deletion src/adapters/MeetingsJSONAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import {MeetingsAdapter, MeetingControlState} from '@webex/component-adapter-int

// Defined meeting controls in Meetings JSON Adapter
export const MUTE_AUDIO_CONTROL = 'mute-audio';
export const JOIN_CONTROL = 'join-meeting';
export const DISABLED_MUTE_AUDIO_CONTROL = 'disabled-mute-audio';
export const DISABLED_JOIN_CONTROL = 'disabled-join-meeting';

/**
* @typedef MeetingsJSON
Expand Down Expand Up @@ -49,6 +52,24 @@ export default class MeetingsJSONAdapter extends MeetingsAdapter {
action: this.toggleMuteAudio.bind(this),
display: this.muteAudioControl.bind(this),
};

this.meetingControls[JOIN_CONTROL] = {
ID: JOIN_CONTROL,
action: () => {},
display: this.joinControl.bind(this),
};

this.meetingControls[DISABLED_MUTE_AUDIO_CONTROL] = {
ID: DISABLED_MUTE_AUDIO_CONTROL,
action: () => {},
display: this.disabledMuteAudioControl.bind(this),
};

this.meetingControls[DISABLED_JOIN_CONTROL] = {
ID: DISABLED_JOIN_CONTROL,
action: () => {},
display: this.disabledJoinControl.bind(this),
};
}

/**
Expand Down Expand Up @@ -114,7 +135,7 @@ export default class MeetingsJSONAdapter extends MeetingsAdapter {
* Returns an observable that emits the display data of a meeting control.
*
* @param {string} ID ID of the meeting for which to update display
* @returns {Observable<MeetingControlDisplay>}
* @returns {Observable.<MeetingControlDisplay>}
* @memberof MeetingJSONAdapter
* @private
*/
Expand Down Expand Up @@ -171,4 +192,67 @@ export default class MeetingsJSONAdapter extends MeetingsAdapter {
}
}
}

/**
* Returns an observable that emits the display data of a meeting control.
*
* @param {string} ID ID of the meeting for which to update display
* @returns {Observable.<MeetingControlDisplay>}
* @memberof MeetingJSONAdapter
* @private
*/
joinControl() {
return Observable.create((observer) => {
observer.next({
ID: JOIN_CONTROL,
text: 'Join meeting',
tooltip: 'Join meeting',
state: MeetingControlState.ACTIVE,
});

observer.complete();
});
}

/**
* Returns an observable that emits the display data of a disabled meeting control.
*
* @param {string} ID ID of the meeting for which to update display
* @returns {Observable.<MeetingControlDisplay>}
* @memberof MeetingJSONAdapter
* @private
*/
disabledJoinControl() {
return Observable.create((observer) => {
observer.next({
ID: JOIN_CONTROL,
text: 'Join meeting',
tooltip: 'Join meeting',
state: MeetingControlState.DISABLED,
});

observer.complete();
});
}

/**
* Returns an observable that emits the display data of a disabled meeting control.
*
* @param {string} ID ID of the meeting for which to update display
* @returns {Observable.<MeetingControlDisplay>}
* @memberof MeetingJSONAdapter
* @private
*/
disabledMuteAudioControl() {
return Observable.create((observer) => {
observer.next({
ID: JOIN_CONTROL,
icon: 'microphone',
tooltip: 'Mute disabled',
state: MeetingControlState.DISABLED,
});

observer.complete();
});
}
}
39 changes: 39 additions & 0 deletions src/components/WebexMeetingControl/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Webex Meeting Control Component

Webex Meeting Control component displays a button that allows for control over a meeting by performing a specific action.

<p align="center">
<img src="./WebexMeetingControl.png" alt="Default Webex Meeting Control" />
</p>

## Preview

To see all the different possible states of the Webex Meeting Control component, you can run our Storybook:

```shell
npm start
```

## Embed

1. Create a component adapter from which the data will be retrieved (See [adapters](../../adapters)). For instance:

```js
const jsonAdapter = new WebexJSONAdapter(jsonData);
```

2. Create a component instance by passing the control _type_ as a string and
enclose it within [a data provider](../WebexDataProvider/WebexDataProvider.js)
that takes the [component data adapter](../../adapters/WebexJSONAdapter.js) that we created previously.
A Webex Meeting Control must also be enclosed by a [Webex Meeting Controls](./WebexMeetingControls.js) component that contains the
meeting context for the control

```js
<WebexDataProvider adapter={jsonAdapter}>
<WebexMeetingControls meetingID="meetingID">
<WebexMeetingControl type="mute-audio" />
</WebexMeetingControls>
</WebexDataProvider>
```

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.
39 changes: 39 additions & 0 deletions src/components/WebexMeetingControl/WebexMeetingControl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';
import PropTypes from 'prop-types';
import {Button, Icon} from '@momentum-ui/react';
import {MeetingControlState} from '@webex/component-adapter-interfaces';

import {useMeetingControl} from '../hooks';

/**
* WebexMeetingControl component represents an action that can
* be taken in a meeting.
*
* @param {object} props
* @returns {object} JSX of the component
*/
export default function WebexMeetingControl({type}) {
const [action, display] = useMeetingControl(type);
const {icon, text, tooltip} = display;
const isDisabled = display.state === MeetingControlState.DISABLED;
const iconColor = display.state === MeetingControlState.ACTIVE ? 'red' : '';
let button = (
<Button color="green" size={52} ariaLabel={tooltip} onClick={action} disabled={isDisabled}>
{text}
</Button>
);

if (icon) {
button = (
<Button circle color={iconColor} size={56} ariaLabel={tooltip} onClick={action} disabled={isDisabled}>
<Icon name={`${icon}_28`} />
</Button>
);
}

return button;
}

WebexMeetingControl.propTypes = {
type: PropTypes.string.isRequired,
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 51 additions & 0 deletions src/components/WebexMeetingControl/WebexMeetingControl.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react';
import {storiesOf} from '@storybook/react';

import jsonData from '../../data';
import {WebexJSONAdapter} from '../../adapters';
import {WebexDataProvider, WebexMeetingControl, WebexMeetingControls} from '../';

// Setup for the stories
const stories = storiesOf('Webex Meeting Control', module);
const webexAdapter = new WebexJSONAdapter(jsonData);

// Stories
stories.add('text', () => (
<WebexDataProvider adapter={webexAdapter}>
<WebexMeetingControls meetingID="scheduledMeeting">
<WebexMeetingControl type="join-meeting" />
</WebexMeetingControls>
</WebexDataProvider>
));

stories.add('disabled text', () => (
<WebexDataProvider adapter={webexAdapter}>
<WebexMeetingControls meetingID="scheduledMeeting">
<WebexMeetingControl type="disabled-join-meeting" />
</WebexMeetingControls>
</WebexDataProvider>
));

stories.add('active icon', () => (
<WebexDataProvider adapter={webexAdapter}>
<WebexMeetingControls meetingID="mutedLocalAudio">
<WebexMeetingControl type="mute-audio" />
</WebexMeetingControls>
</WebexDataProvider>
));

stories.add('inactive icon', () => (
<WebexDataProvider adapter={webexAdapter}>
<WebexMeetingControls meetingID="localAudio">
<WebexMeetingControl type="mute-audio" />
</WebexMeetingControls>
</WebexDataProvider>
));

stories.add('disabled icon', () => (
<WebexDataProvider adapter={webexAdapter}>
<WebexMeetingControls meetingID="mutedLocalAudio">
<WebexMeetingControl type="disabled-mute-audio" />
</WebexMeetingControls>
</WebexDataProvider>
));
29 changes: 29 additions & 0 deletions src/components/WebexMeetingControl/WebexMeetingControl.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';

import WebexMeetingControl from './WebexMeetingControl';

jest.mock('../hooks/useMeetingControl');

describe('Webex Meeting Control component', () => {
describe('snapshot', () => {
test('matches with text button', () => {
expect(shallow(<WebexMeetingControl type="join-meeting" />)).toMatchSnapshot();
});

test('matches with disabled text button', () => {
expect(shallow(<WebexMeetingControl type="join-meeting-disabled" />)).toMatchSnapshot();
});

test('matches with active icon button', () => {
expect(shallow(<WebexMeetingControl type="mute-audio-active" />)).toMatchSnapshot();
});

test('matches with inactive icon button', () => {
expect(shallow(<WebexMeetingControl type="mute-audio-inactive" />)).toMatchSnapshot();
});

test('matches with disabled icon button', () => {
expect(shallow(<WebexMeetingControl type="mute-audio-disabled" />)).toMatchSnapshot();
});
});
});
Loading

0 comments on commit b4fcf5a

Please sign in to comment.