Skip to content

Commit

Permalink
Add loading indicator and welcome message on initial load to username…
Browse files Browse the repository at this point in the history
… modal
  • Loading branch information
jimmcgaw committed Feb 27, 2023
1 parent 59d246b commit 13b15f0
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 9 deletions.
15 changes: 14 additions & 1 deletion src/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import ChatContainer from './chat/ChatContainer';
import PincodeModal from './modals/PincodeModal';
import UsernameModal from './modals/Username';

const isNewRoom = Boolean(!document.location.hash);

class App extends Component {
constructor(props) {
super(props);
Expand Down Expand Up @@ -90,7 +92,11 @@ class App extends Component {
const {
username,
pincodeRequired,
previousUsername } = this.props;
previousUsername,
authenticating,
connecting,
connected,
} = this.props;

let showUsernameModal = this.state.modals.username.isVisible;
showUsernameModal = !pincodeRequired && (showUsernameModal || username === '');
Expand All @@ -112,7 +118,11 @@ class App extends Component {
previousUsername={previousUsername}
username={username}
isVisible={showUsernameModal}
isNewRoom={isNewRoom}
setUsername={this.props.setUsername}
authenticating={authenticating}
connecting={connecting}
connected={connected}
onToggleModalVisibility={this.onToggleModalVisibility} />}

<main className="encloser">
Expand All @@ -136,6 +146,9 @@ const mapStateToProps = (reduxState) => {
previousUsername: reduxState.chat.previousUsername,
pincodeRequired: reduxState.chat.pincodeRequired,
shouldConnect: reduxState.chat.shouldConnect,
connecting: reduxState.chat.connecting,
connected: reduxState.chat.connected,
authenticating: reduxState.chat.authenticating,
};
};

Expand Down
41 changes: 39 additions & 2 deletions src/components/modals/Username.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useRef, useState } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';

import { Modal, Button } from 'react-bootstrap';
import { Modal, Button, Alert, ProgressBar } from 'react-bootstrap';

import { generateRandomUsername } from '../../data/username';

Expand All @@ -12,11 +12,15 @@ export const MAX_USERNAME_LENGTH = 45;

const UsernameModal = ({
isVisible,
isNewRoom,
previousUsername,
username,
onToggleModalVisibility,
setUsername,
enableAudio,
authenticating,
connecting,
connected,
}) => {
const usernameInput = useRef(null);

Expand Down Expand Up @@ -66,6 +70,22 @@ const UsernameModal = ({
}
};

let progress = 0;
let statusMessage = (
<p>Cranking a bunch of gears.</p>
);

if (authenticating) {
progress = 60;
statusMessage = <p>Establishing a session in your browser.</p>;
} else if (connecting) {
progress = 80;
statusMessage = <p>Creating a secure connection with LeapChat servers.</p>;
} else if (connected) {
progress = 95;
statusMessage = <p>Connected!</p>;
}

return (
<div>
<Modal show={isVisible} onHide={onClose}>
Expand All @@ -74,6 +94,15 @@ const UsernameModal = ({
</Modal.Header>
<Modal.Body>
<div data-testid="set-username-form" className="form-group">
{/* username is empty on initial page load, not on subsequent 'edit username' opens */}
{!username && isNewRoom && <Alert
bsStyle="success">
New room created!
</Alert>}
{!username && !isNewRoom && <Alert
bsStyle="success">
Successfully joined room!
</Alert>}
<label htmlFor="username">Username</label>
<input
id="username"
Expand All @@ -92,10 +121,14 @@ const UsernameModal = ({
</div>}
<Button bsSize="xsmall" bsStyle="primary" onClick={setRandomUsernameInForm}>Generate Random Username</Button>
</div>
{!connected && <div className="progress-indicator">
{statusMessage}
<ProgressBar active now={progress} label={`${progress}%`} />
</div>}
</Modal.Body>
<Modal.Footer>
{username && <Button onClick={onClose}>Cancel</Button>}
<Button data-testid="set-username" onClick={onSetUsername} bsStyle="primary">Set Username</Button>
<Button data-testid="set-username" onClick={onSetUsername} bsStyle="primary" disabled={!connected}>Set Username</Button>
</Modal.Footer>
</Modal>
</div>
Expand All @@ -104,10 +137,14 @@ const UsernameModal = ({

UsernameModal.propTypes = {
isVisible: PropTypes.bool.isRequired,
isNewRoom: PropTypes.bool.isRequired,
previousUsername: PropTypes.string.isRequired,
username: PropTypes.string.isRequired,
onToggleModalVisibility: PropTypes.func.isRequired,
setUsername: PropTypes.func.isRequired,
authenticating: PropTypes.bool.isRequired,
connecting: PropTypes.bool.isRequired,
connected: PropTypes.bool.isRequired,
};

const mapDispatchToProps = (dispatch) => {
Expand Down
16 changes: 10 additions & 6 deletions test/playwright/SetUsername.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ import { test, expect } from '@playwright/test';
import { MAX_USERNAME_LENGTH } from '../../src/components/modals/Username';

const username = "LeapChatUser";
const testFragment = "UnnoticedNuisanceSuperstoreDazzlerSpurYahooRerunIglooWoolworkerDustpanRhinoUnjustlyCadillacEnergySlingshotUpkeepCopierPurseTurtleCuisineDehydratorOpulentlyHabitatElongatedAmplifier";

test.beforeEach(async ({ page }) => {
await page.goto("http://localhost:8080/");
});

test.describe('Sets an initial user name', () => {
test('sets username', async ({ page }) => {
await expect(page.locator(".form-group .alert-success")).toBeVisible();
await expect(page.locator(".form-group .alert-success")).toHaveText(/New room created/);

await page.locator("#username").fill(username);
await page.getByTestId("set-username").click();

Expand All @@ -19,25 +23,25 @@ test.describe('Sets an initial user name', () => {
});

test("sees an error if username is empty", async ({ page }) => {
await expect(page.getByRole("alert")).not.toBeVisible();
await expect(page.locator(".alert-danger")).not.toBeVisible();

await page.locator("#username").fill("");
await page.getByTestId("set-username").click();

await expect(page.getByRole("alert")).toBeVisible();
await expect(page.getByRole("alert")).toHaveText(/Must not be empty/);
await expect(page.locator(".form-group .alert-danger")).toBeVisible();
await expect(page.locator(".form-group .alert-danger")).toHaveText(/Must not be empty/);
});

test("sees an error if username is too long", async ({ page }) => {
await expect(page.getByRole("alert")).not.toBeVisible();
await expect(page.locator(".alert-danger")).not.toBeVisible();

const tooLongUsername = new Array(MAX_USERNAME_LENGTH + 3).join("X");

await page.locator("#username").fill(tooLongUsername);
await page.getByTestId("set-username").click();

await expect(page.getByRole("alert")).toBeVisible();
await expect(page.locator(".alert-danger")).toBeVisible();
const expectedError = new RegExp(`Length must not exceed ${MAX_USERNAME_LENGTH}`);
await expect(page.getByRole("alert")).toHaveText(expectedError);
await expect(page.locator(".alert-danger")).toHaveText(expectedError);
});
});

0 comments on commit 13b15f0

Please sign in to comment.