Skip to content

Commit e422878

Browse files
No Scheduling page when bookings link is empty (#138)
* frequently asked questions * moving styles * review comments
1 parent 88245d7 commit e422878

File tree

6 files changed

+302
-19
lines changed

6 files changed

+302
-19
lines changed

client/src/Book.test.tsx

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { AppConfigModel } from './models/ConfigModel';
1010
import * as FetchConfig from './utils/FetchConfig';
1111
import { runFakeTimers } from './utils/TestUtils';
1212
import { generateTheme } from './utils/ThemeGenerator';
13+
import * as renderer from 'react-test-renderer';
1314
import { BOOKINGS_SPECIMEN_URL } from './utils/Constants';
1415
import WarningBanner from './components/Book/WarningBanner';
1516

@@ -56,7 +57,7 @@ describe('Book', () => {
5657
expect(genericError.length).toBe(1);
5758
});
5859

59-
it('should render header and bookings iframe when config is loaded', async () => {
60+
it('should render header and bookings iframe when config is loaded and bookings link is not empty', async () => {
6061
const mockBookingsUrl = 'https://example.org';
6162
const fetchConfigSpy = jest.spyOn(FetchConfig, 'fetchConfig');
6263
fetchConfigSpy.mockReturnValue(
@@ -91,12 +92,12 @@ describe('Book', () => {
9192
expect(iframes.first().props().src).toBe(mockBookingsUrl);
9293
});
9394

94-
it('should render warning banner if using specimen Bookings page', async () => {
95+
it('should render header and no scheduling info when config is loaded and bookings link is empty', async () => {
9596
const fetchConfigSpy = jest.spyOn(FetchConfig, 'fetchConfig');
9697
fetchConfigSpy.mockReturnValue(
9798
Promise.resolve({
9899
communicationEndpoint: 'endpoint=test_endpoint;',
99-
microsoftBookingsUrl: BOOKINGS_SPECIMEN_URL,
100+
microsoftBookingsUrl: '',
100101
chatEnabled: true,
101102
screenShareEnabled: true,
102103
companyName: '',
@@ -118,10 +119,64 @@ describe('Book', () => {
118119
const iframes = book.find('iframe');
119120
const warningBanner = book.find(WarningBanner);
120121

122+
expect(spinners.length).toBe(0);
123+
expect(warningBanner.length).toBe(0);
124+
expect(headers.length).toBe(1);
125+
expect(iframes.length).toBe(0);
126+
});
127+
128+
it('should render warning banner if using specimen Bookings page', async () => {
129+
const fetchConfigSpy = jest.spyOn(FetchConfig, 'fetchConfig');
130+
fetchConfigSpy.mockReturnValue(
131+
Promise.resolve({
132+
communicationEndpoint: 'endpoint=test_endpoint;',
133+
microsoftBookingsUrl: BOOKINGS_SPECIMEN_URL,
134+
chatEnabled: true,
135+
screenShareEnabled: true,
136+
companyName: '',
137+
theme: generateTheme('#FFFFFF'),
138+
waitingTitle: '',
139+
waitingSubtitle: '',
140+
logoUrl: ''
141+
} as AppConfigModel)
142+
);
143+
144+
const book = await mount(<Book />);
145+
146+
await runFakeTimers();
147+
book.update();
148+
149+
const warningBanner = book.find(WarningBanner);
150+
151+
const spinners = book.find(Spinner);
152+
const headers = book.find(Header);
153+
const iframes = book.find('iframe');
154+
121155
expect(spinners.length).toBe(0);
122156
expect(warningBanner.length).toBe(1);
123157
expect(headers.length).toBe(1);
124158
expect(iframes.length).toBe(1);
125159
expect(iframes.first().props().src).toBe(BOOKINGS_SPECIMEN_URL);
126160
});
161+
162+
it('should match snapshot when bookings link is empty', async () => {
163+
const fetchConfigSpy = jest.spyOn(FetchConfig, 'fetchConfig');
164+
fetchConfigSpy.mockReturnValue(
165+
Promise.resolve({
166+
communicationEndpoint: 'endpoint=test_endpoint;',
167+
microsoftBookingsUrl: '',
168+
chatEnabled: true,
169+
screenShareEnabled: true,
170+
companyName: '',
171+
theme: generateTheme('#FFFFFF'),
172+
waitingTitle: '',
173+
waitingSubtitle: '',
174+
logoUrl: ''
175+
} as AppConfigModel)
176+
);
177+
const book = renderer.create(<Book />);
178+
await runFakeTimers();
179+
180+
expect(book.toJSON()).toMatchSnapshot();
181+
});
127182
});

client/src/Book.tsx

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,23 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT license.
33

4-
import { LayerHost, Spinner, Stack, ThemeProvider } from '@fluentui/react';
4+
import { Spinner, Stack, ThemeProvider } from '@fluentui/react';
55
import { backgroundStyles, fullSizeStyles } from './styles/Common.styles';
6-
import { embededIframeStyles } from './styles/Book.styles';
76
import { Header } from './Header';
87
import './styles/Common.css';
98
import { fetchConfig } from './utils/FetchConfig';
109
import { AppConfigModel } from './models/ConfigModel';
1110
import { GenericError } from './components/GenericError';
1211
import { useEffect, useState } from 'react';
13-
import { BOOKINGS_SPECIMEN_URL } from './utils/Constants';
14-
import WarningBanner from './components/Book/WarningBanner';
15-
16-
const PARENT_ID = 'BookMeetingSection';
12+
import { BookingsPage } from './components/Book/BookingsPage';
13+
import { NoSchedulingPage } from './components/Book/NoSchedulingPage';
1714

1815
export const Book = (): JSX.Element => {
1916
const [config, setConfig] = useState<AppConfigModel | undefined>(undefined);
2017
const [error, setError] = useState<any | undefined>(undefined);
2118

19+
const PARENT_ID = 'BookMeetingSection';
20+
2221
useEffect(() => {
2322
const fetchData = async (): Promise<void> => {
2423
try {
@@ -38,16 +37,7 @@ export const Book = (): JSX.Element => {
3837
<ThemeProvider theme={config.theme} style={{ height: '100%' }}>
3938
<Stack styles={backgroundStyles(config.theme)}>
4039
<Header companyName={config.companyName} parentid={PARENT_ID} />
41-
<LayerHost
42-
id={PARENT_ID}
43-
style={{
44-
position: 'relative',
45-
height: '100%'
46-
}}
47-
>
48-
{config.microsoftBookingsUrl === BOOKINGS_SPECIMEN_URL ? <WarningBanner /> : <></>}
49-
<iframe src={config.microsoftBookingsUrl} scrolling="yes" style={embededIframeStyles}></iframe>
50-
</LayerHost>
40+
{config.microsoftBookingsUrl ? <BookingsPage config={config} /> : <NoSchedulingPage config={config} />}
5141
</Stack>
5242
</ThemeProvider>
5343
);
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`Book should match snapshot when bookings link is empty 1`] = `
4+
<div
5+
className="body-118"
6+
style={
7+
Object {
8+
"height": "100%",
9+
}
10+
}
11+
>
12+
<div
13+
className="ms-Stack css-119"
14+
>
15+
<div
16+
className="ms-Stack css-120"
17+
>
18+
<div>
19+
<button
20+
aria-label="Menu Button"
21+
className="ms-Button ms-Button--icon root-121"
22+
data-is-focusable={true}
23+
id="waffle-menu"
24+
onClick={[Function]}
25+
onKeyDown={[Function]}
26+
onKeyPress={[Function]}
27+
onKeyUp={[Function]}
28+
onMouseDown={[Function]}
29+
onMouseUp={[Function]}
30+
type="button"
31+
>
32+
<span
33+
className="ms-Button-flexContainer flexContainer-122"
34+
data-automationid="splitbuttonprimary"
35+
>
36+
<i
37+
aria-hidden={true}
38+
className="ms-Icon root-105 ms-Button-icon icon-124"
39+
data-icon-name="Waffle"
40+
style={
41+
Object {
42+
"fontFamily": undefined,
43+
}
44+
}
45+
/>
46+
</span>
47+
</button>
48+
</div>
49+
<span
50+
className="css-128"
51+
>
52+
53+
</span>
54+
</div>
55+
<div
56+
className="ms-Stack css-129"
57+
>
58+
<div
59+
className="ms-Stack css-130"
60+
>
61+
<div
62+
className="ms-Stack css-131"
63+
>
64+
<span
65+
className="css-132"
66+
>
67+
No scheduling configured
68+
</span>
69+
<span
70+
className="css-133"
71+
>
72+
This sample does not have a Microsoft Bookings page configured.
73+
</span>
74+
<span
75+
className="css-134"
76+
>
77+
Frequently asked questions
78+
</span>
79+
<div
80+
className="ms-Stack css-135"
81+
>
82+
<a
83+
className="ms-Link root-136"
84+
href="https://aka.ms/virtual-appointments-sample-bookings"
85+
onClick={[Function]}
86+
tabIndex={0}
87+
target="_blank"
88+
>
89+
<div
90+
className="ms-Stack css-137"
91+
>
92+
<div
93+
className="ms-StackItem css-138"
94+
style={
95+
Object {
96+
"fontFamily": "sf pro text",
97+
"fontSize": ".9375rem",
98+
"fontWeight": "400",
99+
"letterSpacing": "-0.015rem",
100+
"lineHeight": "1.25rem",
101+
"paddingRight": ".5rem",
102+
"textDecoration": "underline",
103+
"verticalAlign": "bottom",
104+
}
105+
}
106+
>
107+
How do I change my Microsoft Bookings page URL?
108+
</div>
109+
<div
110+
className="ms-StackItem css-138"
111+
style={
112+
Object {
113+
"textDecoration": "none",
114+
}
115+
}
116+
>
117+
<span
118+
style={
119+
Object {
120+
"alignItems": "center",
121+
"display": "flex",
122+
}
123+
}
124+
>
125+
<i
126+
aria-hidden={true}
127+
className="root-105"
128+
data-icon-name="OpenInNewWindow"
129+
/>
130+
</span>
131+
</div>
132+
</div>
133+
</a>
134+
</div>
135+
</div>
136+
</div>
137+
</div>
138+
</div>
139+
</div>
140+
`;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
4+
import { LayerHost } from '@fluentui/react';
5+
import { BOOKINGS_SPECIMEN_URL } from '../../utils/Constants';
6+
import WarningBanner from './WarningBanner';
7+
import { AppConfigModel } from '../../models/ConfigModel';
8+
import { embededIframeStyles } from '../../styles/Book.styles';
9+
10+
const PARENT_ID = 'BookMeetingSection';
11+
12+
interface BookingsPageProps {
13+
config: AppConfigModel;
14+
}
15+
16+
export const BookingsPage = (props: BookingsPageProps): JSX.Element => {
17+
return (
18+
<LayerHost
19+
id={PARENT_ID}
20+
style={{
21+
position: 'relative',
22+
height: '100%'
23+
}}
24+
>
25+
{props.config.microsoftBookingsUrl === BOOKINGS_SPECIMEN_URL ? <WarningBanner /> : <></>}
26+
<iframe src={props.config.microsoftBookingsUrl} scrolling="yes" style={embededIframeStyles}></iframe>
27+
</LayerHost>
28+
);
29+
};
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
import {
4+
containerStyles,
5+
fullScreenStyles,
6+
innerContainer,
7+
lineHeight22px,
8+
lineHeight28px
9+
} from '../../styles/Book.styles';
10+
import { Stack, Text } from '@fluentui/react';
11+
import { FrequentlyAskedQuestions } from '../FrequentlyAskedQuestions';
12+
import { AppConfigModel } from '../../models/ConfigModel';
13+
14+
interface NoSchedulingPageProps {
15+
config: AppConfigModel;
16+
}
17+
18+
export const NoSchedulingPage = (props: NoSchedulingPageProps): JSX.Element => {
19+
return (
20+
<Stack styles={fullScreenStyles}>
21+
<Stack horizontalAlign="center" styles={containerStyles(props.config.theme)} tokens={{ childrenGap: 15 }}>
22+
<Stack styles={innerContainer}>
23+
<Text styles={lineHeight28px}>No scheduling configured</Text>
24+
<Text styles={lineHeight22px}>This sample does not have a Microsoft Bookings page configured.</Text>
25+
<FrequentlyAskedQuestions />
26+
</Stack>
27+
</Stack>
28+
</Stack>
29+
);
30+
};

client/src/styles/Book.styles.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,47 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT license.
33

4+
import { PartialTheme, Theme } from '@fluentui/theme';
5+
46
export const embededIframeStyles = {
57
width: '100%',
68
height: '100%',
79
border: '0px'
810
};
11+
12+
export const fullScreenStyles = {
13+
root: {
14+
width: '100%',
15+
height: '100%'
16+
}
17+
};
18+
19+
export function containerStyles(theme: PartialTheme | Theme | undefined): any {
20+
return {
21+
root: {
22+
maxWidth: '64rem',
23+
width: '100%',
24+
height: '100%',
25+
display: 'flex',
26+
margin: 'auto',
27+
marginTop: '38px',
28+
backgroundColor: 'white',
29+
borderRadius: theme?.effects?.roundedCorner4
30+
}
31+
};
32+
}
33+
34+
export const innerContainer = {
35+
root: {
36+
width: '600px',
37+
marginTop: '200px'
38+
}
39+
};
40+
41+
export const lineHeight28px = {
42+
root: { fontWeight: '600', fontSize: '20px', lineHeight: '28px' }
43+
};
44+
45+
export const lineHeight22px = {
46+
root: { fontWeight: '600', fontSize: '16px', lineHeight: '22px', marginBottom: '16px' }
47+
};

0 commit comments

Comments
 (0)