Migration: Settings forms (#24741)

* Migrate shared preferences

* Remove unused import

* Migrate org name form

* Update TeamSettings

* Convert user settings

* Fix inital default values

* Update snapshots

* Fix CI errors

* Fix failing Ci

* Update snapshots

* Large spacing

* Move use Form for OrgSettings and add FieldSet

* Remove snapshots

* Add snapshots

* Remove unused prop
This commit is contained in:
Tobias Skarhed 2020-06-24 10:55:39 +02:00 committed by GitHub
parent 1e38a24b88
commit d3299796f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 92 additions and 235 deletions

View File

@ -23,6 +23,7 @@ export { UnitPicker } from './UnitPicker/UnitPicker';
export { StatsPicker } from './StatsPicker/StatsPicker'; export { StatsPicker } from './StatsPicker/StatsPicker';
export { RefreshPicker } from './RefreshPicker/RefreshPicker'; export { RefreshPicker } from './RefreshPicker/RefreshPicker';
export { TimeRangePicker } from './TimePicker/TimeRangePicker'; export { TimeRangePicker } from './TimePicker/TimeRangePicker';
export { TimeZonePicker } from './TimePicker/TimeZonePicker';
export { TimeOfDayPicker } from './TimePicker/TimeOfDayPicker'; export { TimeOfDayPicker } from './TimePicker/TimeOfDayPicker';
export { List } from './List/List'; export { List } from './List/List';
export { TagsInput } from './TagsInput/TagsInput'; export { TagsInput } from './TagsInput/TagsInput';

View File

@ -10,6 +10,7 @@ import { loadOrganization, updateOrganization } from './state/actions';
import { Organization, StoreState } from 'app/types'; import { Organization, StoreState } from 'app/types';
import { getNavModel } from 'app/core/selectors/navModel'; import { getNavModel } from 'app/core/selectors/navModel';
import { setOrganizationName } from './state/reducers'; import { setOrganizationName } from './state/reducers';
import { VerticalGroup } from '@grafana/ui';
export interface Props { export interface Props {
navModel: NavModel; navModel: NavModel;
@ -24,11 +25,8 @@ export class OrgDetailsPage extends PureComponent<Props> {
await this.props.loadOrganization(); await this.props.loadOrganization();
} }
onOrgNameChange = (name: string) => { onUpdateOrganization = (orgName: string) => {
this.props.setOrganizationName(name); this.props.setOrganizationName(orgName);
};
onUpdateOrganization = () => {
this.props.updateOrganization(); this.props.updateOrganization();
}; };
@ -40,14 +38,10 @@ export class OrgDetailsPage extends PureComponent<Props> {
<Page navModel={navModel}> <Page navModel={navModel}>
<Page.Contents isLoading={isLoading}> <Page.Contents isLoading={isLoading}>
{!isLoading && ( {!isLoading && (
<div> <VerticalGroup>
<OrgProfile <OrgProfile onSubmit={this.onUpdateOrganization} orgName={organization.name} />
onOrgNameChange={name => this.onOrgNameChange(name)}
onSubmit={this.onUpdateOrganization}
orgName={organization.name}
/>
<SharedPreferences resourceUri="org" /> <SharedPreferences resourceUri="org" />
</div> </VerticalGroup>
)} )}
</Page.Contents> </Page.Contents>
</Page> </Page>

View File

@ -6,7 +6,6 @@ const setup = () => {
const props: Props = { const props: Props = {
orgName: 'Main org', orgName: 'Main org',
onSubmit: jest.fn(), onSubmit: jest.fn(),
onOrgNameChange: jest.fn(),
}; };
return shallow(<OrgProfile {...props} />); return shallow(<OrgProfile {...props} />);

View File

@ -1,43 +1,28 @@
import React, { ChangeEvent, FC } from 'react'; import React, { FC } from 'react';
import { LegacyForms } from '@grafana/ui'; import { Input, Field, FieldSet, Button, Form } from '@grafana/ui';
const { Input } = LegacyForms;
export interface Props { export interface Props {
orgName: string; orgName: string;
onSubmit: () => void; onSubmit: (orgName: string) => void;
onOrgNameChange: (orgName: string) => void;
} }
const OrgProfile: FC<Props> = ({ onSubmit, onOrgNameChange, orgName }) => { interface FormDTO {
orgName: string;
}
const OrgProfile: FC<Props> = ({ onSubmit, orgName }) => {
return ( return (
<div> <Form defaultValues={{ orgName }} onSubmit={({ orgName }: FormDTO) => onSubmit(orgName)}>
<h3 className="page-sub-heading">Organization profile</h3> {({ register }) => (
<form <FieldSet label="Organization profile">
name="orgForm" <Field label="Organization name">
className="gf-form-group" <Input name="orgName" type="text" ref={register({ required: true })} />
onSubmit={event => { </Field>
event.preventDefault();
onSubmit(); <Button type="submit">Update organization name</Button>
}} </FieldSet>
> )}
<div className="gf-form-inline"> </Form>
<div className="gf-form max-width-28">
<span className="gf-form-label">Organization name</span>
<Input
className="gf-form-input"
type="text"
onChange={(event: ChangeEvent<HTMLInputElement>) => onOrgNameChange(event.target.value)}
value={orgName}
/>
</div>
</div>
<div className="gf-form-button-row">
<button type="submit" className="btn btn-primary">
Save
</button>
</div>
</form>
</div>
); );
}; };

View File

@ -35,16 +35,15 @@ exports[`Render should render organization and preferences 1`] = `
<PageContents <PageContents
isLoading={false} isLoading={false}
> >
<div> <Component>
<OrgProfile <OrgProfile
onOrgNameChange={[Function]}
onSubmit={[Function]} onSubmit={[Function]}
orgName="Cool org" orgName="Cool org"
/> />
<SharedPreferences <SharedPreferences
resourceUri="org" resourceUri="org"
/> />
</div> </Component>
</PageContents> </PageContents>
</Page> </Page>
`; `;

View File

@ -1,46 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Render should render component 1`] = ` exports[`Render should render component 1`] = `
<div> <Form
<h3 defaultValues={
className="page-sub-heading" Object {
> "orgName": "Main org",
Organization profile }
</h3> }
<form onSubmit={[Function]}
className="gf-form-group" >
name="orgForm" <Component />
onSubmit={[Function]} </Form>
>
<div
className="gf-form-inline"
>
<div
className="gf-form max-width-28"
>
<span
className="gf-form-label"
>
Organization name
</span>
<Input
className="gf-form-input"
onChange={[Function]}
type="text"
value="Main org"
/>
</div>
</div>
<div
className="gf-form-button-row"
>
<button
className="btn btn-primary"
type="submit"
>
Save
</button>
</div>
</form>
</div>
`; `;

View File

@ -12,7 +12,7 @@ const setup = (propOverrides?: object) => {
Object.assign(props, propOverrides); Object.assign(props, propOverrides);
const wrapper = shallow(<TeamSettings {...props} />); const wrapper = shallow(<TeamSettings {...props} />);
const instance = wrapper.instance() as TeamSettings; const instance = wrapper.instance() as any;
return { return {
wrapper, wrapper,
@ -27,18 +27,3 @@ describe('Render', () => {
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
}); });
}); });
describe('Functions', () => {
it('should update team', () => {
const { instance } = setup();
const mockEvent = { preventDefault: jest.fn() };
instance.setState({
name: 'test11',
});
instance.onUpdate(mockEvent);
expect(instance.props.updateTeam).toHaveBeenCalledWith('test11', 'test@test.com');
});
});

View File

@ -1,7 +1,6 @@
import React from 'react'; import React, { FC } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { InlineFormLabel, LegacyForms } from '@grafana/ui'; import { Input, Field, Form, Button, FieldSet, VerticalGroup } from '@grafana/ui';
const { Input } = LegacyForms;
import { SharedPreferences } from 'app/core/components/SharedPreferences/SharedPreferences'; import { SharedPreferences } from 'app/core/components/SharedPreferences/SharedPreferences';
import { updateTeam } from './state/actions'; import { updateTeam } from './state/actions';
@ -14,78 +13,37 @@ export interface Props {
updateTeam: typeof updateTeam; updateTeam: typeof updateTeam;
} }
interface State { export const TeamSettings: FC<Props> = ({ team, updateTeam }) => {
name: string; return (
email: string; <VerticalGroup>
} <FieldSet label="Team Settings">
<Form
defaultValues={{ ...team }}
onSubmit={(formTeam: Team) => {
updateTeam(formTeam.name, formTeam.email);
}}
>
{({ register }) => (
<>
<Field label="Name">
<Input name="name" ref={register({ required: true })} />
</Field>
export class TeamSettings extends React.Component<Props, State> { <Field
constructor(props: Props) { label="Email"
super(props); description="This is optional and is primarily used to set the team profile avatar (via gravatar service)"
>
this.state = { <Input placeholder="team@email.com" type="email" name="email" ref={register} />
name: props.team.name, </Field>
email: props.team.email, <Button type="submit">Update</Button>
}; </>
} )}
</Form>
onChangeName = (event: any) => { </FieldSet>
this.setState({ name: event.target.value }); <SharedPreferences resourceUri={`teams/${team.id}`} />
}; </VerticalGroup>
);
onChangeEmail = (event: any) => { };
this.setState({ email: event.target.value });
};
onUpdate = (event: any) => {
const { name, email } = this.state;
event.preventDefault();
this.props.updateTeam(name, email);
};
render() {
const { team } = this.props;
const { name, email } = this.state;
return (
<div>
<h3 className="page-sub-heading">Team Settings</h3>
<form name="teamDetailsForm" className="gf-form-group" onSubmit={this.onUpdate}>
<div className="gf-form max-width-30">
<InlineFormLabel>Name</InlineFormLabel>
<Input
type="text"
required
value={name}
className="gf-form-input max-width-22"
onChange={this.onChangeName}
/>
</div>
<div className="gf-form max-width-30">
<InlineFormLabel tooltip="This is optional and is primarily used to set the team profile avatar (via gravatar service)">
Email
</InlineFormLabel>
<Input
type="email"
className="gf-form-input max-width-22"
value={email}
placeholder="team@email.com"
onChange={this.onChangeEmail}
/>
</div>
<div className="gf-form-button-row">
<button type="submit" className="btn btn-primary">
Update
</button>
</div>
</form>
<SharedPreferences resourceUri={`teams/${team.id}`} />
</div>
);
}
}
function mapStateToProps(state: any) { function mapStateToProps(state: any) {
const teamId = getRouteParamsId(state.location); const teamId = getRouteParamsId(state.location);

View File

@ -44,7 +44,7 @@ exports[`Render should render settings and preferences page 1`] = `
<PageContents <PageContents
isLoading={true} isLoading={true}
> >
<Connect(TeamSettings) /> <Connect(Component) />
</PageContents> </PageContents>
</Page> </Page>
`; `;
@ -66,7 +66,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render s
<PageContents <PageContents
isLoading={true} isLoading={true}
> >
<Connect(TeamSettings) /> <Connect(Component) />
</PageContents> </PageContents>
</Page> </Page>
`; `;

View File

@ -1,60 +1,28 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Render should render component 1`] = ` exports[`Render should render component 1`] = `
<div> <Component>
<h3 <Component
className="page-sub-heading" label="Team Settings"
> >
Team Settings <Form
</h3> defaultValues={
<form Object {
className="gf-form-group" "avatarUrl": "some/url/",
name="teamDetailsForm" "email": "test@test.com",
onSubmit={[Function]} "id": 1,
> "memberCount": 1,
<div "name": "test",
className="gf-form max-width-30" "permission": 0,
}
}
onSubmit={[Function]}
> >
<Component> <Component />
Name </Form>
</Component> </Component>
<Input
className="gf-form-input max-width-22"
onChange={[Function]}
required={true}
type="text"
value="test"
/>
</div>
<div
className="gf-form max-width-30"
>
<Component
tooltip="This is optional and is primarily used to set the team profile avatar (via gravatar service)"
>
Email
</Component>
<Input
className="gf-form-input max-width-22"
onChange={[Function]}
placeholder="team@email.com"
type="email"
value="test@test.com"
/>
</div>
<div
className="gf-form-button-row"
>
<button
className="btn btn-primary"
type="submit"
>
Update
</button>
</div>
</form>
<SharedPreferences <SharedPreferences
resourceUri="teams/1" resourceUri="teams/1"
/> />
</div> </Component>
`; `;