Chore: Remove Form usage from admin components (#81618)

* Chore: Remove Form usage from AdminEditOrgPage

* Chore: Remove Form usage from UserCreatePage.tsx

* Chore: Transform LdapPage to FC

* Chore: Remove Form usage from LdapPage.tsx
This commit is contained in:
Alex Khomenko
2024-02-01 07:58:50 +02:00
committed by GitHub
parent 177fa1b947
commit a416bd761d
3 changed files with 142 additions and 143 deletions
+21 -18
View File
@@ -1,8 +1,9 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { useAsyncFn } from 'react-use'; import { useAsyncFn } from 'react-use';
import { NavModelItem } from '@grafana/data'; import { NavModelItem } from '@grafana/data';
import { Form, Field, Input, Button, Legend, Alert } from '@grafana/ui'; import { Field, Input, Button, Legend, Alert } from '@grafana/ui';
import { Page } from 'app/core/components/Page/Page'; import { Page } from 'app/core/components/Page/Page';
import { contextSrv } from 'app/core/core'; import { contextSrv } from 'app/core/core';
import { GrafanaRouteComponentProps } from 'app/core/navigation/types'; import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
@@ -27,6 +28,11 @@ const AdminEditOrgPage = ({ match }: Props) => {
const [totalPages, setTotalPages] = useState(1); const [totalPages, setTotalPages] = useState(1);
const [orgState, fetchOrg] = useAsyncFn(() => getOrg(orgId), []); const [orgState, fetchOrg] = useAsyncFn(() => getOrg(orgId), []);
const {
handleSubmit,
register,
formState: { errors },
} = useForm<OrgNameDTO>();
const [, fetchOrgUsers] = useAsyncFn(async (page) => { const [, fetchOrgUsers] = useAsyncFn(async (page) => {
const result = await getOrgUsers(orgId, page); const result = await getOrgUsers(orgId, page);
@@ -45,8 +51,8 @@ const AdminEditOrgPage = ({ match }: Props) => {
fetchOrgUsers(page); fetchOrgUsers(page);
}, [fetchOrg, fetchOrgUsers, page]); }, [fetchOrg, fetchOrgUsers, page]);
const onUpdateOrgName = async (name: string) => { const onUpdateOrgName = async ({ orgName }: OrgNameDTO) => {
await updateOrgName(name, orgId); await updateOrgName(orgName, orgId);
}; };
const renderMissingPermissionMessage = () => ( const renderMissingPermissionMessage = () => (
@@ -82,21 +88,18 @@ const AdminEditOrgPage = ({ match }: Props) => {
<> <>
<Legend>Edit organization</Legend> <Legend>Edit organization</Legend>
{orgState.value && ( {orgState.value && (
<Form <form onSubmit={handleSubmit(onUpdateOrgName)} style={{ maxWidth: '600px' }}>
defaultValues={{ orgName: orgState.value.name }} <Field label="Name" invalid={!!errors.orgName} error="Name is required" disabled={!canWriteOrg}>
onSubmit={(values: OrgNameDTO) => onUpdateOrgName(values.orgName)} <Input
> {...register('orgName', { required: true })}
{({ register, errors }) => ( id="org-name-input"
<> defaultValue={orgState.value.name}
<Field label="Name" invalid={!!errors.orgName} error="Name is required" disabled={!canWriteOrg}> />
<Input {...register('orgName', { required: true })} id="org-name-input" /> </Field>
</Field> <Button type="submit" disabled={!canWriteOrg}>
<Button type="submit" disabled={!canWriteOrg}> Update
Update </Button>
</Button> </form>
</>
)}
</Form>
)} )}
<div style={{ marginTop: '20px' }}> <div style={{ marginTop: '20px' }}>
+33 -38
View File
@@ -1,9 +1,10 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import { useForm } from 'react-hook-form';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { NavModelItem } from '@grafana/data'; import { NavModelItem } from '@grafana/data';
import { getBackendSrv } from '@grafana/runtime'; import { getBackendSrv } from '@grafana/runtime';
import { Form, Button, Input, Field } from '@grafana/ui'; import { Button, Input, Field } from '@grafana/ui';
import { Page } from 'app/core/components/Page/Page'; import { Page } from 'app/core/components/Page/Page';
interface UserDTO { interface UserDTO {
@@ -24,6 +25,11 @@ const pageNav: NavModelItem = {
const UserCreatePage = () => { const UserCreatePage = () => {
const history = useHistory(); const history = useHistory();
const {
handleSubmit,
register,
formState: { errors },
} = useForm<UserDTO>({ mode: 'onBlur' });
const onSubmit = useCallback( const onSubmit = useCallback(
async (data: UserDTO) => { async (data: UserDTO) => {
@@ -37,45 +43,34 @@ const UserCreatePage = () => {
return ( return (
<Page navId="global-users" pageNav={pageNav}> <Page navId="global-users" pageNav={pageNav}>
<Page.Contents> <Page.Contents>
<Form onSubmit={onSubmit} validateOn="onBlur"> <form onSubmit={handleSubmit(onSubmit)} style={{ maxWidth: '600px' }}>
{({ register, errors }) => { <Field label="Name" required invalid={!!errors.name} error={errors.name ? 'Name is required' : undefined}>
return ( <Input id="name-input" {...register('name', { required: true })} />
<> </Field>
<Field
label="Name"
required
invalid={!!errors.name}
error={errors.name ? 'Name is required' : undefined}
>
<Input id="name-input" {...register('name', { required: true })} />
</Field>
<Field label="Email"> <Field label="Email">
<Input id="email-input" {...register('email')} /> <Input id="email-input" {...register('email')} />
</Field> </Field>
<Field label="Username"> <Field label="Username">
<Input id="username-input" {...register('login')} /> <Input id="username-input" {...register('login')} />
</Field> </Field>
<Field <Field
label="Password" label="Password"
required required
invalid={!!errors.password} invalid={!!errors.password}
error={errors.password ? 'Password is required and must contain at least 4 characters' : undefined} error={errors.password ? 'Password is required and must contain at least 4 characters' : undefined}
> >
<Input <Input
id="password-input" id="password-input"
{...register('password', { {...register('password', {
validate: (value) => value.trim() !== '' && value.length >= 4, validate: (value) => value.trim() !== '' && value.length >= 4,
})} })}
type="password" type="password"
/> />
</Field> </Field>
<Button type="submit">Create user</Button> <Button type="submit">Create user</Button>
</> </form>
);
}}
</Form>
</Page.Contents> </Page.Contents>
</Page> </Page>
); );
+88 -87
View File
@@ -1,9 +1,10 @@
import React, { PureComponent } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { connect, ConnectedProps } from 'react-redux'; import { connect, ConnectedProps } from 'react-redux';
import { NavModelItem } from '@grafana/data'; import { NavModelItem } from '@grafana/data';
import { featureEnabled } from '@grafana/runtime'; import { featureEnabled } from '@grafana/runtime';
import { Alert, Button, Field, Form, Input, Stack } from '@grafana/ui'; import { Alert, Button, Field, Input, Stack } from '@grafana/ui';
import { Page } from 'app/core/components/Page/Page'; import { Page } from 'app/core/components/Page/Page';
import { contextSrv } from 'app/core/core'; import { contextSrv } from 'app/core/core';
import { GrafanaRouteComponentProps } from 'app/core/navigation/types'; import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
@@ -37,10 +38,6 @@ interface OwnProps extends GrafanaRouteComponentProps<{}, { username?: string }>
userError?: LdapError; userError?: LdapError;
} }
interface State {
isLoading: boolean;
}
interface FormModel { interface FormModel {
username: string; username: string;
} }
@@ -52,101 +49,105 @@ const pageNav: NavModelItem = {
id: 'LDAP', id: 'LDAP',
}; };
export class LdapPage extends PureComponent<Props, State> { export const LdapPage = ({
state = { clearUserMappingInfo,
isLoading: true, queryParams,
}; loadLdapState,
loadLdapSyncStatus,
loadUserMapping,
clearUserError,
ldapUser,
userError,
ldapError,
ldapSyncInfo,
ldapConnectionInfo,
}: Props) => {
const [isLoading, setIsLoading] = useState(true);
const { register, handleSubmit } = useForm<FormModel>();
async componentDidMount() { const fetchUserMapping = useCallback(
const { clearUserMappingInfo, queryParams } = this.props; async (username: string) => {
await clearUserMappingInfo(); return loadUserMapping(username);
await this.fetchLDAPStatus(); },
[loadUserMapping]
);
if (queryParams.username) { useEffect(() => {
await this.fetchUserMapping(queryParams.username); const fetchLDAPStatus = async () => {
return Promise.all([loadLdapState(), loadLdapSyncStatus()]);
};
async function init() {
await clearUserMappingInfo();
await fetchLDAPStatus();
if (queryParams.username) {
await fetchUserMapping(queryParams.username);
}
setIsLoading(false);
} }
this.setState({ isLoading: false }); init();
} }, [clearUserMappingInfo, fetchUserMapping, loadLdapState, loadLdapSyncStatus, queryParams]);
async fetchLDAPStatus() { const search = ({ username }: FormModel) => {
const { loadLdapState, loadLdapSyncStatus } = this.props;
return Promise.all([loadLdapState(), loadLdapSyncStatus()]);
}
async fetchUserMapping(username: string) {
const { loadUserMapping } = this.props;
return await loadUserMapping(username);
}
search = (username: string) => {
if (username) { if (username) {
this.fetchUserMapping(username); fetchUserMapping(username);
} }
}; };
onClearUserError = () => { const onClearUserError = () => {
this.props.clearUserError(); clearUserError();
}; };
render() { const canReadLDAPUser = contextSrv.hasPermission(AccessControlAction.LDAPUsersRead);
const { ldapUser, userError, ldapError, ldapSyncInfo, ldapConnectionInfo, queryParams } = this.props; return (
const { isLoading } = this.state; <Page navId="authentication" pageNav={pageNav}>
const canReadLDAPUser = contextSrv.hasPermission(AccessControlAction.LDAPUsersRead); <Page.Contents isLoading={isLoading}>
<Stack direction="column" gap={4}>
{ldapError && ldapError.title && (
<Alert title={ldapError.title} severity={AppNotificationSeverity.Error}>
{ldapError.body}
</Alert>
)}
return ( <LdapConnectionStatus ldapConnectionInfo={ldapConnectionInfo} />
<Page navId="authentication" pageNav={pageNav}>
<Page.Contents isLoading={isLoading}>
<Stack direction="column" gap={4}>
{ldapError && ldapError.title && (
<Alert title={ldapError.title} severity={AppNotificationSeverity.Error}>
{ldapError.body}
</Alert>
)}
<LdapConnectionStatus ldapConnectionInfo={ldapConnectionInfo} /> {featureEnabled('ldapsync') && ldapSyncInfo && <LdapSyncInfo ldapSyncInfo={ldapSyncInfo} />}
{featureEnabled('ldapsync') && ldapSyncInfo && <LdapSyncInfo ldapSyncInfo={ldapSyncInfo} />} {canReadLDAPUser && (
<section>
{canReadLDAPUser && ( <h3>Test user mapping</h3>
<section> <form onSubmit={handleSubmit(search)}>
<h3>Test user mapping</h3> <Field label="Username">
<Form onSubmit={(data: FormModel) => this.search(data.username)}> <Input
{({ register }) => ( {...register('username', { required: true })}
<Field label="Username"> width={34}
<Input id="username"
{...register('username', { required: true })} type="text"
width={34} defaultValue={queryParams.username}
id="username" addonAfter={
type="text" <Button variant="primary" type="submit">
defaultValue={queryParams.username} Run
addonAfter={ </Button>
<Button variant="primary" type="submit"> }
Run />
</Button> </Field>
} </form>
/> {userError && userError.title && (
</Field> <Alert title={userError.title} severity={AppNotificationSeverity.Error} onRemove={onClearUserError}>
)} {userError.body}
</Form> </Alert>
{userError && userError.title && ( )}
<Alert {ldapUser && <LdapUserInfo ldapUser={ldapUser} />}
title={userError.title} </section>
severity={AppNotificationSeverity.Error} )}
onRemove={this.onClearUserError} </Stack>
> </Page.Contents>
{userError.body} </Page>
</Alert> );
)} };
{ldapUser && <LdapUserInfo ldapUser={ldapUser} />}
</section>
)}
</Stack>
</Page.Contents>
</Page>
);
}
}
const mapStateToProps = (state: StoreState) => ({ const mapStateToProps = (state: StoreState) => ({
ldapConnectionInfo: state.ldap.connectionInfo, ldapConnectionInfo: state.ldap.connectionInfo,