Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "prerelease",
"area": "feature",
"workstream": "Gallery layouts",
"comment": "Introduce storybook docs for gallery layouts",
"packageName": "@azure/communication-react",
"email": "94866715+dmceachernmsft@users.noreply.github.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "prerelease",
"area": "feature",
"workstream": "Gallery layouts",
"comment": "Introduce storybook docs for gallery layouts",
"packageName": "@azure/communication-react",
"email": "94866715+dmceachernmsft@users.noreply.github.com",
"dependentChangeType": "patch"
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
50 changes: 49 additions & 1 deletion packages/storybook/stories/CallComposite/CallCompositeDocs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,55 @@ export const Docs: () => JSX.Element = () => {
gallery](./?path=/docs/ui-components-videogallery--video-gallery) component docs for more information on our
local video tile and some of the other options we have for the local video tile when just using the components.
</Description>

<Heading>Customizing the Gallery Layout</Heading>
Comment thread
dmceachernmsft marked this conversation as resolved.
Outdated
<SingleLineBetaBanner />
<Description>
We allow for the customization of the starting layout of the gallery. The layout can be changed by the user
though the gallery options menu found in the more button of the call controls.
</Description>
<Stack horizontal horizontalAlign="space-between" tokens={{ childrenGap: '1rem' }}>
<Stack>
<Stack horizontalAlign="center">
<img
style={{ width: '100%', maxWidth: '25rem' }}
src="images/callcomposite-dynamic-layout.png"
alt="Dynamic layout for composite video gallery"
/>
<Description>Call Composite with `dynamic` layout.</Description>
</Stack>
<Stack horizontalAlign="center">
<img
style={{ width: '100%', maxWidth: '25rem' }}
src="images/callcomposite-focused-content.png"
alt="Focused content layout for composite video gallery"
/>
<Description>Call Composite with `focused content` layout.</Description>
</Stack>
</Stack>
<Stack>
<Stack horizontalAlign="center">
<img
style={{ width: '100%', maxWidth: '25rem' }}
src="images/callcomposite-gallery-layout.png"
alt="Gallery layout for composite video gallery"
/>
<Description>Call Composite with `gallery` layout.</Description>
</Stack>
<Stack horizontalAlign="center">
<img
style={{ width: '100%', maxWidth: '25rem' }}
src="images/callcomposite-speaker-layout.png"
alt="Speaker layout for composite video gallery"
/>
<Description>Call Composite with `speaker` layout.</Description>
</Stack>
</Stack>
</Stack>
<Description>You can set the gallery layout using the following: </Description>
<Source code="<CallComposite options={galleryOptions: {layout: 'speaker'}} />" />
<Source code="<CallComposite options={galleryOptions: {layout: 'default'}} />" />
<Source code="<CallComposite options={galleryOptions: {layout: 'floatingLocalVideo'}} />" />
<Source code="<CallComposite options={galleryOptions: {layout: 'focusedContent'}} />" />
<div ref={refExistedJoinCall}>
<Heading>Joining an existing Call</Heading>
<Description>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { CustomAvatarVideoGalleryExample } from './snippets/CustomAvatar.snippet
import { CustomStyleVideoGalleryExample } from './snippets/CustomStyle.snippet';
import { DefaultVideoGalleryExample } from './snippets/Default.snippet';
import { FloatingLocalVideoExample } from './snippets/FloatingLocalVideo.snippet';
import { FocusedContentExample } from './snippets/FocusedContent.snippet';
import { LocalCameraSwitcherExample } from './snippets/LocalCameraSwitcher.snippet';
import { ManagedPinnedParticipantsExample } from './snippets/ManagedPinnedParticipants.snippet';
import { MobileWrapper } from './snippets/MobileWrapper';
Expand All @@ -25,13 +26,15 @@ import { PinnedParticipantsDisabledExample } from './snippets/PinnedParticipants
import { PinnedParticipantsMobileExample } from './snippets/PinnedParticipantsMobile.snippet';
import { ScreenSharingFromPresenterExample } from './snippets/ScreenSharingFromPresenter.snippet';
import { ScreenSharingFromViewerExample } from './snippets/ScreenSharingFromViewer.snippet';
import { SpeakerLayoutExample } from './snippets/SpeakerLayout.snippet';
import { WithHorizontalGalleryExample } from './snippets/WithHorizontalGallery.snippet';
import { WithVerticalGalleryExample } from './snippets/WithVerticalGallery.snippet';

const CustomAvatarVideoGalleryExampleText = require('!!raw-loader!./snippets/CustomAvatar.snippet.tsx').default;
const CustomStyleVideoGalleryExampleText = require('!!raw-loader!./snippets/CustomStyle.snippet.tsx').default;
const DefaultVideoGalleryExampleText = require('!!raw-loader!./snippets/Default.snippet.tsx').default;
const FloatingLocalVideoExampleText = require('!!raw-loader!./snippets/FloatingLocalVideo.snippet.tsx').default;
const focusedContentExampleText = require('!!raw-loader!./snippets/FocusedContent.snippet.tsx').default;
const LocalVideoCameraCycleButtonExampleText =
require('!!raw-loader!./snippets/LocalCameraSwitcher.snippet.tsx').default;
const ManagedPinnedParticipantsExampleText =
Expand All @@ -45,6 +48,7 @@ const ScreenSharingFromPresenterExampleText =
require('!!raw-loader!./snippets/ScreenSharingFromPresenter.snippet.tsx').default;
const ScreenSharingFromViewerExampleText =
require('!!raw-loader!./snippets/ScreenSharingFromViewer.snippet.tsx').default;
const speakerLayoutExampleText = require('!!raw-loader!./snippets/SpeakerLayout.snippet.tsx').default;
const WithHorizontalGalleryExampleText = require('!!raw-loader!./snippets/WithHorizontalGallery.snippet.tsx').default;
const WithVerticalGalleryExampleText = require('!!raw-loader!./snippets/WithVerticalGallery.snippet.tsx').default;

Expand All @@ -67,7 +71,29 @@ const getDocs: () => JSX.Element = () => {
<Source code={importStatement} />

<Heading>Layouts</Heading>
<Subheading>Default Layout</Subheading>
<Description>
This feature allows users to choose from a variety of video gallery layouts. On desktop web, the layouts enabled
include default Gallery, Gallery on Top, Focus Mode, and Speaker Mode. On mobile web, the default gallery and
large gallery are enabled. This feature will greatly enhance the user experience and provide more flexibility in
how users view and interact with video content. Enabling this feature will enable greater productivity and allow
for a better flow of discussions and conversations within calls. Here are some example scenarios where custom
layouts are useful:
</Description>
<ul className={'sbdocs sbdocs-p'}>
<li>
During a meeting with many participants such as a conference call, the large gallery layout can be selected to
have greater visibility of the users in a call.{' '}
</li>
<li>
During a patient/doctor call in which the patient wants to show something, the doctor could enable focus mode
to hide all other video tiles and focus on the speakers video stream.{' '}
</li>
<li>
In a call with multiple important people that are talking, the speaker mode can be selected which will switch
the focus between each speaker as they talk.{' '}
</li>
</ul>
<Subheading>Gallery Layout</Subheading>
<Description>
If there are no remote video streams on, all participants are placed in the [Grid
Layout](./?path=/docs/ui-components-gridlayout--grid-layout) including the local user. Otherwise, only remote
Expand All @@ -93,6 +119,27 @@ const getDocs: () => JSX.Element = () => {
<Canvas mdxSource={FloatingLocalVideoExampleText}>
<FloatingLocalVideoExample />
</Canvas>
<Subheading>Speaker layout</Subheading>
<SingleLineBetaBanner></SingleLineBetaBanner>
<Description>
This Layout is meant to highlight the current dominant speaker in the call. For this view in the video gallery
Comment thread
dmceachernmsft marked this conversation as resolved.
Outdated
the only participant that is in the grid view is the participant talking. All other participants are in the
overflow gallery. When screen sharing the screenshare will replace this participant and the overflow gallery
will behave like normal.
</Description>
<Canvas mdxSource={speakerLayoutExampleText}>
<SpeakerLayoutExample />
</Canvas>
<Subheading>Focused Content Layout</Subheading>
<SingleLineBetaBanner></SingleLineBetaBanner>
<Description>
This layout is meant to highlight the current screenshare stream. In this view when the screenshare is present
the other participants will be removed from the grid view and the overflow gallery will be hidden from view.
This allows for the focus of the local participant to be only on the screenshare stream.
</Description>
<Canvas mdxSource={focusedContentExampleText}>
<FocusedContentExample />
</Canvas>

<Heading>Overflow Gallery</Heading>
<DetailedBetaBanner></DetailedBetaBanner>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { VideoGallery } from '@azure/communication-react';
import { Stack } from '@fluentui/react';
import React from 'react';

const MockLocalParticipant = {
userId: 'user1',
displayName: 'You',
state: 'Connected',
isMuted: true
};
const mockVideoElement = document.createElement('div');
mockVideoElement.style.width = decodeURIComponent('100%25');
mockVideoElement.style.height = decodeURIComponent('100%25');
mockVideoElement.style.textAlign = 'center';
const imageElement = document.createElement('img');
imageElement.src = 'images/screenshare-example.png';
imageElement.style.maxWidth = decodeURIComponent('100%25');
imageElement.style.maxHeight = decodeURIComponent('100%25');
mockVideoElement.appendChild(imageElement);
const mockScreenShareStream = {
isAvailable: true,
renderElement: mockVideoElement as HTMLElement
};

const MockRemoteParticipants = [
{
userId: 'user2',
displayName: 'Peter Parker',
isScreenSharingOn: true,
screenShareStream: mockScreenShareStream
},
{
userId: 'user3',
displayName: 'Thor'
},
{
userId: 'user4',
displayName: 'Matthew Murdock'
},
{
userId: 'user5',
displayName: 'Bruce Wayne'
}
];

// This must be the only named export from this module, and must be named to match the storybook path suffix.
// This ensures that storybook hoists the story instead of creating a folder with a single entry.
export const FocusedContentExample: () => JSX.Element = () => {
const containerStyle = { height: '50vh' };
return (
<Stack style={containerStyle}>
<VideoGallery
layout="focusedContent"
localParticipant={MockLocalParticipant}
remoteParticipants={MockRemoteParticipants}
/>
</Stack>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { VideoGallery } from '@azure/communication-react';
import { Stack } from '@fluentui/react';
import React from 'react';

const MockLocalParticipant = {
userId: 'user1',
displayName: 'You',
state: 'Connected',
isMuted: true
};

const MockRemoteParticipants = [
{
userId: 'user2',
displayName: 'Peter Parker',
isSpeaking: true
},
{
userId: 'user3',
displayName: 'Thor'
},
{
userId: 'user4',
displayName: 'Matthew Murdock'
},
{
userId: 'user5',
displayName: 'Bruce Wayne'
}
];

// This must be the only named export from this module, and must be named to match the storybook path suffix.
// This ensures that storybook hoists the story instead of creating a folder with a single entry.
export const SpeakerLayoutExample: () => JSX.Element = () => {
const containerStyle = { height: '50vh' };
return (
<Stack style={containerStyle}>
<VideoGallery
layout="speaker"
localParticipant={MockLocalParticipant}
remoteParticipants={MockRemoteParticipants}
/>
</Stack>
);
};