Skip to content

Commit

Permalink
feat: extract various components (#868)
Browse files Browse the repository at this point in the history
* feat(frontend) add delete confirmation modal

* ref(frontend) move form status message to a separate component

* ref(frontend) move form input to a separate component

* fix(frontend) allow fullName be empty in user edit form

* fix(frontend) fix sidebar active routes display

* feat(frontend) add statusmessage and inputfield stories

* fix(frontend) remove unnecessary console log

* feat(frontend) add inputfield form usage stroy

* fix(frontend) change status message prop naming

* refactor(frontend) change status message style formation

* fix(frontend) fix input field label typo

* ref(frontend) add sweetalert wrapper

Co-authored-by: Egor Mozheiko <egormozheiko@gmail.com>
  • Loading branch information
shaleynikov and EgorMozheiko committed Feb 28, 2022
1 parent dc9b3e7 commit fb4c2fc
Show file tree
Hide file tree
Showing 22 changed files with 334 additions and 185 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,8 @@
"sass-loader": "^9.0.2",
"style-loader": "^3.2.1",
"svg-url-loader": "^7.1.1",
"sweetalert2": "^11.4.0",
"sweetalert2-react-content": "^4.2.0",
"timezone-mock": "^1.3.0",
"true-myth": "^5.1.2",
"ts-essentials": "^9.0.0",
Expand Down
43 changes: 43 additions & 0 deletions stories/InputField.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/* eslint-disable react/jsx-props-no-spreading */
import React from 'react';
import InputField from '@ui/InputField';
import Button from '@ui/Button';
import { ComponentStory, ComponentMeta } from '@storybook/react';

const Template: ComponentStory<typeof InputField> = (args) => (
<InputField type="password" {...args} />
);

export default {
title: 'Components/InputField',
component: InputField,
argTypes: {
type: {
options: ['text', 'password'],
control: { type: 'select' },
},
},
} as ComponentMeta<typeof InputField>;

export const AsFormInput = ({ type, label, placeholder }) => {
return (
<form>
<p>Example of component usage in a form</p>
<InputField type={type} label={label} placeholder={placeholder} />
<InputField type={type} label={label} placeholder={placeholder} />
<Button>Submit</Button>
</form>
);
};
AsFormInput.args = {
label: 'Sample text',
placeholder: 'Sample text',
type: 'text',
};

export const Inputfield = Template.bind({});
Inputfield.args = {
label: 'Sample text',
placeholder: 'Sample text',
type: 'text',
};
19 changes: 19 additions & 0 deletions stories/StatusMessage.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/* eslint-disable react/jsx-props-no-spreading */
import React from 'react';
import StatusMessage from '@ui/StatusMessage';
import { ComponentStory, ComponentMeta } from '@storybook/react';

const Template: ComponentStory<typeof StatusMessage> = (args) => (
<StatusMessage {...args} />
);

export default {
title: 'Components/StatusMessage',
component: StatusMessage,
} as ComponentMeta<typeof StatusMessage>;

export const Statusmessage = Template.bind({});
Statusmessage.args = {
type: 'success',
message: 'Example message',
};
52 changes: 26 additions & 26 deletions webapp/javascript/components/Settings/APIKeys/APIKeyAddForm.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import React, { useState } from 'react';
import Button from '@ui/Button';
import InputField from '@ui/InputField';
import { CopyToClipboard } from 'react-copy-to-clipboard';
import { faCopy } from '@fortawesome/free-solid-svg-icons/faCopy';
import { faCheck } from '@fortawesome/free-solid-svg-icons/faCheck';
import { createAPIKey } from '@pyroscope/redux/reducers/settings';
import { useAppDispatch } from '@pyroscope/redux/hooks';
import { type APIKey } from '@models/apikeys';
import Dropdown, { MenuItem } from '@ui/Dropdown';
import StatusMessage from '@ui/StatusMessage';
import { addNotification } from '@pyroscope/redux/reducers/notifications';
import styles from './APIKeyForm.module.css';

Expand Down Expand Up @@ -65,14 +67,15 @@ function APIKeyAddForm() {
<>
<h2>Add API Key</h2>

<div>{form.errors.join(', ')}</div>
<StatusMessage type="error" message={form.errors.join(', ')} />
<form onSubmit={handleFormSubmit}>
{key ? (
<div>
<div className={styles.success}>
Key has been successfully added. Click the button below to copy it
to clipboard
</div>
<StatusMessage
type="success"
message="Key has been successfully added. Click the button below to copy
it."
/>
<div>
<CopyToClipboard text={key} onCopy={handleKeyCopy}>
<Button icon={faCopy} className={styles.keyOutput}>
Expand All @@ -82,17 +85,16 @@ function APIKeyAddForm() {
</div>
</div>
) : (
<div className={styles.addForm}>
<div>
<h4>Name</h4>
<input
id="keyName"
type="text"
name="name"
value={form.name}
onChange={handleFormChange}
/>
</div>
<>
<InputField
label="Name"
placeholder="Name"
id="keyName"
type="text"
name="name"
value={form.name}
onChange={handleFormChange}
/>
<div>
<h4>Role</h4>
<Dropdown
Expand All @@ -105,21 +107,19 @@ function APIKeyAddForm() {
<MenuItem value="Agent">Agent</MenuItem>
</Dropdown>
</div>
<div>
<h4>Valid For (seconds):</h4>
<input
id="keyTTL"
name="ttlSeconds"
value={form.ttlSeconds}
onChange={handleFormChange}
/>
</div>
<InputField
label="Valid for (seconds):"
id="keyTTL"
name="ttlSeconds"
value={form.ttlSeconds}
onChange={handleFormChange}
/>
<div>
<Button icon={faCheck} type="submit" kind="secondary">
Add API Key
</Button>
</div>
</div>
</>
)}
</form>
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,3 @@
.addForm {
}

.addForm > div:not(:last-child) {
display: flex;
margin: 10px 0px;
flex-direction: column;
width: 30%;
min-width: 150px;
}

.success {
background-color: #28a745;
color: white;
padding: 10px;
margin: 15px 0;
}

.keyOutput {
max-width: 40%;
}
11 changes: 9 additions & 2 deletions webapp/javascript/components/Settings/APIKeys/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useEffect } from 'react';
import Button from '@ui/Button';
import Icon from '@ui/Icon';
import confirmDelete from '@ui/Modals/ConfirmDelete';

import { useAppDispatch, useAppSelector } from '@pyroscope/redux/hooks';
import { faTimes } from '@fortawesome/free-solid-svg-icons/faTimes';
Expand All @@ -24,14 +25,20 @@ const ApiKeys = () => {
dispatch(reloadApiKeys());
}, []);

const handleDelete = (key) => {
const onDelete = (key) => {
dispatch(deleteAPIKey(key))
.unwrap()
.then(() => {
dispatch(reloadApiKeys());
});
};

const handleDeleteClick = (key) => {
confirmDelete('this key', () => {
onDelete(key);
});
};

const now = new Date();
return (
<>
Expand Down Expand Up @@ -73,7 +80,7 @@ const ApiKeys = () => {
type="submit"
kind="danger"
aria-label="Delete key"
onClick={() => handleDelete(key)}
onClick={() => handleDeleteClick(key)}
>
<Icon icon={faTimes} />
</Button>
Expand Down
10 changes: 0 additions & 10 deletions webapp/javascript/components/Settings/Preferences.module.css

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import {
} from '@pyroscope/redux/reducers/user';
import { addNotification } from '@pyroscope/redux/reducers/notifications';

import styles from './Preferences.module.css';
import StatusMessage from '@ui/StatusMessage';
import InputField from '@ui/InputField';

function Preferences(props) {
const { currentUser } = props;
Expand Down Expand Up @@ -47,43 +48,34 @@ function Preferences(props) {
<>
<h2>Edit profile</h2>
<form onSubmit={handleFormSubmit}>
<div className={styles.errors}>{form.errors}</div>
<div className={styles.preferencesInputWrapper}>
<h4>Username</h4>
<input
type="text"
placeholder="username"
value={form?.name}
name="name"
required
disabled={isEditDisabled}
onChange={handleFormChange}
/>
</div>

<div className={styles.preferencesInputWrapper}>
<h4>Full Name</h4>
<input
type="text"
placeholder="Full Name"
name="fullName"
value={form?.fullName}
required
onChange={handleFormChange}
/>
</div>

<div className={styles.preferencesInputWrapper}>
<h4>Email</h4>
<input
type="text"
placeholder="email"
value={form?.email}
required
name="email"
onChange={handleFormChange}
/>
</div>
<StatusMessage type="error" message={form.errors} />
<InputField
label="Username"
type="text"
placeholder="username"
value={form?.name}
name="name"
required
disabled={isEditDisabled}
onChange={handleFormChange}
/>
<InputField
label="Full Name"
type="text"
placeholder="Full Name"
name="fullName"
value={form?.fullName}
onChange={handleFormChange}
/>
<InputField
label="Email"
type="text"
placeholder="email"
value={form?.email}
required
name="email"
onChange={handleFormChange}
/>
<Button type="submit" kind="secondary">
Save
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { useAppDispatch } from '@pyroscope/redux/hooks';

import { changeMyPassword } from '@pyroscope/redux/reducers/user';
import { addNotification } from '@pyroscope/redux/reducers/notifications';
import styles from './Security.module.css';
import StatusMessage from '@ui/StatusMessage';
import InputField from '@ui/InputField';

function ChangePasswordForm(props) {
const { user } = props;
Expand Down Expand Up @@ -57,37 +58,31 @@ function ChangePasswordForm(props) {
<h2>Change password</h2>
<div>
<form onSubmit={handleFormSubmit}>
<div className={styles.errors}>{form.errors.join(', ')}</div>
<div className={styles.securityInputWrapper}>
<h4>Old password</h4>
<input
type="password"
placeholder="Password"
name="oldPassword"
required
onChange={handleChange}
/>
</div>
<div className={styles.securityInputWrapper}>
<h4>New password</h4>
<input
type="password"
placeholder="New password"
name="password"
required
onChange={handleChange}
/>
</div>
<div className={styles.securityInputWrapper}>
<h4>Confirm new password</h4>
<input
type="password"
placeholder="New password"
name="passwordAgain"
required
onChange={handleChange}
/>
</div>
<StatusMessage type="error" message={form.errors.join(', ')} />
<InputField
label="Old password"
type="password"
placeholder="Password"
name="oldPassword"
required
onChange={handleChange}
/>
<InputField
label="New password"
type="password"
placeholder="New password"
name="password"
required
onChange={handleChange}
/>
<InputField
label="Confirm new password"
type="password"
placeholder="New password"
name="passwordAgain"
required
onChange={handleChange}
/>
<Button type="submit" kind="secondary">
Save
</Button>
Expand Down

0 comments on commit fb4c2fc

Please sign in to comment.