Merge pull request #503 from grafana/135-token-form

135 - token form
This commit is contained in:
Rares Mardare 2022-09-16 14:58:36 +03:00 committed by GitHub
commit e5e1688bb4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 112 additions and 47 deletions

View file

@ -1,29 +1,29 @@
.root {
position: relative;
width: 100%;
&:hover .copyButton {
opacity: 1;
}
}
.scroller {
overflow-y: auto;
border-radius: 2px;
padding: 12px 60px 12px 20px;
&--maxHeight {
max-height: 400px;
}
}
.scroller_max-height {
max-height: 400px;
}
.root .button {
.copyIcon,
.copyButton {
position: absolute;
top: 15px;
right: 15px;
opacity: 0;
transition: opacity 0.2s ease;
}
.root:hover .button {
opacity: 1;
}
.root pre {
border-radius: 2px;
padding: 12px 20px;
}
.copyButton {
opacity: 0;
}

View file

@ -1,41 +1,47 @@
import React, { FC } from 'react';
import { Button } from '@grafana/ui';
import { Button, Icon, IconButton } from '@grafana/ui';
import cn from 'classnames/bind';
import CopyToClipboard from 'react-copy-to-clipboard';
import { openNotification } from 'utils';
import styles from './SourceCode.module.css';
import styles from './SourceCode.module.scss';
const cx = cn.bind(styles);
interface SourceCodeProps {
noMaxHeight?: boolean;
showClipboardIconOnly?: boolean;
showCopyToClipboard?: boolean;
children?: any
children?: any;
}
const SourceCode: FC<SourceCodeProps> = (props) => {
const { children, noMaxHeight = false, showCopyToClipboard = true } = props;
const { children, noMaxHeight = false, showClipboardIconOnly = false, showCopyToClipboard = true } = props;
const showClipboardCopy = showClipboardIconOnly || showCopyToClipboard;
return (
<div className={cx('root')}>
{showCopyToClipboard && (
{showClipboardCopy && (
<CopyToClipboard
text={children as string}
onCopy={() => {
openNotification('Copied!');
}}
>
<Button className={cx('button')} variant="primary" icon="copy">
Copy
</Button>
{showClipboardIconOnly ? (
<IconButton className={cx('copyIcon')} size={'lg'} name="copy" />
) : (
<Button className={cx('copyButton')} variant="primary" size="xs" icon="copy">
Copy
</Button>
)}
</CopyToClipboard>
)}
<pre
className={cx('scroller', {
'scroller_max-height': !noMaxHeight,
'scroller--maxHeight': !noMaxHeight,
})}
>
<code>{children}</code>

View file

@ -0,0 +1,16 @@
.token__inputContainer {
width: 100%;
display: flex;
margin-bottom: 24px;
}
.token__input {
flex-grow: 1;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.token__copyButton {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}

View file

@ -1,13 +1,20 @@
import React, { useCallback, HTMLAttributes, useState } from 'react';
import { Button, Field, HorizontalGroup, Input, Modal, VerticalGroup } from '@grafana/ui';
import { Button, HorizontalGroup, Input, Label, Modal, VerticalGroup } from '@grafana/ui';
import cn from 'classnames/bind';
import { get } from 'lodash-es';
import { observer } from 'mobx-react';
import CopyToClipboard from 'react-copy-to-clipboard';
import SourceCode from 'components/SourceCode/SourceCode';
import { ApiToken } from 'models/api_token/api_token.types';
import { useStore } from 'state/useStore';
import { openErrorNotification, openNotification } from 'utils';
import { getItem } from 'utils/localStorage';
import styles from './ApiTokenForm.module.css';
const cx = cn.bind(styles);
interface TokenCreationModalProps extends HTMLAttributes<HTMLElement> {
visible: boolean;
@ -16,7 +23,7 @@ interface TokenCreationModalProps extends HTMLAttributes<HTMLElement> {
}
const ApiTokenForm = observer((props: TokenCreationModalProps) => {
const { visible, onHide = () => {}, onUpdate = () => {} } = props;
const { onHide = () => {}, onUpdate = () => {} } = props;
const [name, setName] = useState('');
const [token, setToken] = useState('');
@ -39,30 +46,68 @@ const ApiTokenForm = observer((props: TokenCreationModalProps) => {
return (
<Modal isOpen closeOnEscape={false} title={token ? 'Your new API Token' : 'Create API Token'} onDismiss={onHide}>
<VerticalGroup>
<Input maxLength={50} onChange={handleNameChange} autoFocus placeholder="Enter token name" />
{token && (
<>
<Input value={token} disabled />
</>
)}
<HorizontalGroup>
{token && (
<CopyToClipboard
text={token}
onCopy={() => {
openNotification('Token copied');
}}
>
<Button>Copy Token</Button>
</CopyToClipboard>
)}
<Button disabled={!!token || !name} variant="primary" onClick={onCreateTokenCallback}>
Create
<Label>Token Name</Label>
<div className={cx('token__inputContainer')}>
{renderTokenInput()}
{renderCopyToClipboard()}
</div>
{renderCurlExample()}
<HorizontalGroup justify="flex-end">
<Button variant="secondary" onClick={() => onHide()}>
{token ? 'Close' : 'Cancel'}
</Button>
{!token && (
<Button disabled={!!token || !name} variant="primary" onClick={onCreateTokenCallback}>
Create Token
</Button>
)}
</HorizontalGroup>
</VerticalGroup>
</Modal>
);
function renderTokenInput() {
return token ? (
<Input value={token} disabled={!!token} className={cx('token__input')} />
) : (
<Input
className={cx('token__input')}
maxLength={50}
onChange={handleNameChange}
placeholder="Enter token name"
autoFocus
/>
);
}
function renderCopyToClipboard() {
if (!token) {
return null;
}
return (
<CopyToClipboard text={token} onCopy={() => openNotification('Token copied')}>
<Button className={cx('token__copyButton')}>Copy Token</Button>
</CopyToClipboard>
);
}
function renderCurlExample() {
if (!token) {
return null;
}
return (
<VerticalGroup>
<Label>Curl command example</Label>
<SourceCode showClipboardIconOnly>{getCurlExample(token)}</SourceCode>
</VerticalGroup>
);
}
});
function getCurlExample(token) {
return `curl -H "Authorization: ${token}" ${getItem('onCallApiUrl')}/api/internal/v1/alert_receive_channels`;
}
export default ApiTokenForm;

View file

@ -48,8 +48,6 @@ class ApiTokens extends React.Component<ApiTokensProps, any> {
const apiTokens = apiTokenStore.getSearchResult();
const loading = !apiTokens;
const { showCreateTokenModal } = this.state;
const columns = [