Skip to content

Commit

Permalink
Adapted first user form to be shown in a page instead of a popup (#1306)
Browse files Browse the repository at this point in the history
  • Loading branch information
teclator authored Jun 11, 2024
2 parents c5bf27d + 2f11032 commit 82edb2d
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 74 deletions.
9 changes: 5 additions & 4 deletions web/src/components/users/FirstUser.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*/

import React, { useState, useEffect, useRef } from "react";
import { Link } from "react-router-dom";
import { Link, useNavigate } from "react-router-dom";

import { _ } from "~/i18n";
import { useCancellablePromise } from "~/utils";
Expand All @@ -40,7 +40,7 @@ import {

import { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table';

import { RowActions, PasswordAndConfirmationInput, Popup, If } from '~/components/core';
import { RowActions, PasswordAndConfirmationInput, Popup, If, ButtonLink } from '~/components/core';

import { suggestUsernames } from '~/components/users/utils';

Expand All @@ -53,7 +53,7 @@ const UserNotDefined = ({ actionCb }) => {
{_("Please, be aware that a user must be defined before installing the system to be able to log into it.")}
</strong>
</div>
<Link to="first">{_("Define a user now")}</Link>
<ButtonLink to="first" isPrimary>{_("Define a user now")}</ButtonLink>
</div>
);
};
Expand Down Expand Up @@ -205,11 +205,12 @@ export default function FirstUser() {

const isUserDefined = user?.userName && user?.userName !== "";
const showErrors = () => ((errors || []).length > 0);
const navigate = useNavigate();

const actions = [
{
title: _("Edit"),
onClick: (e) => openForm(e, EDIT_MODE)
onClick: () => navigate('/users/first/edit')
},
{
title: _("Discard"),
Expand Down
10 changes: 5 additions & 5 deletions web/src/components/users/FirstUser.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ let onUsersChangeFn = jest.fn();
const openUserForm = async () => {
const { user } = installerRender(<FirstUser />);
await screen.findByText("No user defined yet.");
const button = await screen.findByRole("button", { name: "Define a user now" });
const button = await screen.findByText("Define a user now");
await user.click(button);
const dialog = await screen.findByRole("dialog");
const dialog = await screen.findByLabelText("Username");

return { user, dialog };
};
Expand All @@ -68,10 +68,10 @@ beforeEach(() => {
it.skip("allows defining a new user", async () => {
const { user } = installerRender(<FirstUser />);
await screen.findByText("No user defined yet.");
const button = await screen.findByRole("button", { name: "Define a user now" });
const button = await screen.findByText("Define a user now");
await user.click(button);

const dialog = await screen.findByRole("dialog");
const dialog = await screen.findByRole("form");

const fullNameInput = within(dialog).getByLabelText("Full name");
await user.type(fullNameInput, "Jane Doe");
Expand Down Expand Up @@ -103,7 +103,7 @@ it.skip("allows defining a new user", async () => {

it.skip("doest not allow to confirm the settings if the user name and the password are not provided", async () => {
const { user } = installerRender(<FirstUser />);
const button = await screen.findByRole("button", { name: "Define a user now" });
const button = await screen.findByText("Define a user now");
await user.click(button);

const dialog = await screen.findByRole("dialog");
Expand Down
151 changes: 93 additions & 58 deletions web/src/components/users/FirstUserForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@ import {
Menu,
MenuContent,
MenuList,
MenuItem
MenuItem,
Card,
Grid,
GridItem,
Stack,
Switch
} from "@patternfly/react-core";

import { Loading } from "~/components/layout";
Expand Down Expand Up @@ -79,17 +84,20 @@ export default function FirstUserForm() {
const [insideDropDown, setInsideDropDown] = useState(false);
const [focusedIndex, setFocusedIndex] = useState(-1);
const [suggestions, setSuggestions] = useState([]);
const [changePassword, setChangePassword] = useState(true);
const usernameInputRef = useRef();
const navigate = useNavigate();
const passwordRef = useRef();

useEffect(() => {
cancellablePromise(client.users.getUser()).then(userValues => {
const editing = userValues.userName !== "";
setState({
load: true,
user: userValues,
isEditing: userValues.username !== ""
isEditing: editing
});
setChangePassword(!editing);
});
}, [client.users, cancellablePromise]);

Expand Down Expand Up @@ -123,8 +131,9 @@ export default function FirstUserForm() {
return user;
}, user);

// Preserve current password value if the user was not editing it.
if (state.isEditing && user.password === "") delete user.password;
if (!changePassword) {
delete user.password;
}
delete user.passwordConfirmation;
user.autologin = !!user.autologin;

Expand Down Expand Up @@ -191,68 +200,94 @@ export default function FirstUserForm() {

return (
<>
<Page.Header>
<h2>{state.isEditing ? _("Edit user") : _("Create user")}</h2>
</Page.Header>

<Page.MainContent>
<Form id="firstUserForm" onSubmit={onSubmit}>
{errors.length > 0 &&
<Alert variant="warning" isInline title={_("Something went wrong")}>
{errors.map((e, i) => <p key={`error_${i}`}>{e}</p>)}
</Alert>}
<Grid hasGutter>
<GridItem sm={12} xl={6} rowSpan={2}>
<Page.CardSection isFullHeight>
<Stack hasGutter>
<FormGroup fieldId="userFullName" label={_("Full name")}>
<TextInput
id="userFullName"
name="fullName"
aria-label={_("User full name")}
defaultValue={state.user.fullName}
label={_("User full name")}
onBlur={(e) => setSuggestions(suggestUsernames(e.target.value))}
/>
</FormGroup>

<FormGroup fieldId="userFullName" label={_("Full name")}>
<TextInput
id="userFullName"
name="fullName"
aria-label={_("User full name")}
defaultValue={state.user.fullName}
label={_("User full name")}
onBlur={(e) => setSuggestions(suggestUsernames(e.target.value))}
/>
</FormGroup>

<FormGroup
className="first-username-wrapper"
fieldId="userName"
label={_("Username")}
isRequired
>
<TextInput
id="userName"
name="userName"
aria-label={_("Username")}
ref={usernameInputRef}
defaultValue={state.user.userName}
label={_("Username")}
isRequired
onFocus={renderSuggestions}
onKeyDown={handleKeyDown}
onBlur={() => !insideDropDown && setShowSuggestions(false)}
/>
<If
condition={showSuggestions}
then={
<UsernameSuggestions
entries={suggestions}
onSelect={onSuggestionSelected}
setInsideDropDown={setInsideDropDown}
focusedIndex={focusedIndex}
<FormGroup
className="first-username-wrapper"
fieldId="userName"
label={_("Username")}
isRequired
>
<TextInput
id="userName"
name="userName"
aria-label={_("Username")}
ref={usernameInputRef}
defaultValue={state.user.userName}
label={_("Username")}
isRequired
onFocus={renderSuggestions}
onKeyDown={handleKeyDown}
onBlur={() => !insideDropDown && setShowSuggestions(false)}
/>
<If
condition={showSuggestions}
then={
<UsernameSuggestions
entries={suggestions}
onSelect={onSuggestionSelected}
setInsideDropDown={setInsideDropDown}
focusedIndex={focusedIndex}
/>
}
/>
</FormGroup>
</Stack>
</Page.CardSection>
</GridItem>
<GridItem sm={12} xl={6}>
<Page.CardSection>
<Stack hasGutter>
{state.isEditing &&
<Switch
label={_("Change password")}
isChecked={changePassword}
onChange={() => setChangePassword(!changePassword)}
/>}
<PasswordAndConfirmationInput
inputRef={passwordRef}
isDisabled={!changePassword}
showErrors={false}
/>
</Stack>
</Page.CardSection>
</GridItem>
<GridItem sm={12} xl={6}>
<Page.CardSection>
<Checkbox
aria-label={_("user autologin")}
id="autologin"
name="autologin"
// TRANSLATORS: check box label
label={_("Auto-login")}
defaultChecked={state.user.autologin}
/>
}
/>
</FormGroup>

<PasswordAndConfirmationInput
inputRef={passwordRef}
showErrors={false}
/>

<Checkbox
aria-label={_("user autologin")}
id="autologin"
name="autologin"
// TRANSLATORS: check box label
label={_("Auto-login")}
defaultChecked={state.user.autologin}
/>
</Page.CardSection>
</GridItem>
</Grid>
</Form>
</Page.MainContent>

Expand Down
35 changes: 28 additions & 7 deletions web/src/components/users/UsersPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,39 @@
import React from "react";

import { _ } from "~/i18n";
import { Section } from "~/components/core";
import { CardField, Page } from "~/components/core";
import { FirstUser, RootAuthMethods } from "~/components/users";
import { Card, CardBody, Grid, GridItem, Stack } from "@patternfly/react-core";

export default function UsersPage() {
return (
<>
<Section title={_("User")} icon="person">
<FirstUser />
</Section>
<Section title={_("Root authentication")} icon="badge">
<RootAuthMethods />
</Section>
<Page.Header>
<h2>{_("Users")}</h2>
</Page.Header>

<Page.MainContent>
<Grid hasGutter>
<GridItem sm={12} xl={6}>
<CardField label={_("First user")}>
<CardBody>
<Stack hasGutter>
<FirstUser />
</Stack>
</CardBody>
</CardField>
</GridItem>
<GridItem sm={12} xl={6}>
<CardField label={_("Root authentication")}>
<CardBody>
<Stack hasGutter>
<RootAuthMethods />
</Stack>
</CardBody>
</CardField>
</GridItem>
</Grid>
</Page.MainContent>
</>
);
}
7 changes: 7 additions & 0 deletions web/src/components/users/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ const routes = {
handle: {
name: _("Create or edit the first user")
}
},
{
path: "first/edit",
element: <FirstUserForm />,
handle: {
name: _("Edit first user")
}
}
]
};
Expand Down

0 comments on commit 82edb2d

Please sign in to comment.