feat: add a new `<SecretInput>` component (#46073)

pull/45478/head
Levente Balogh 3 years ago committed by GitHub
parent 590ea19c3f
commit 47d1d83673
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 60
      packages/grafana-ui/src/components/SecretInput/SecretInput.story.tsx
  2. 65
      packages/grafana-ui/src/components/SecretInput/SecretInput.test.tsx
  3. 26
      packages/grafana-ui/src/components/SecretInput/SecretInput.tsx
  4. 1
      packages/grafana-ui/src/components/SecretInput/index.tsx

@ -0,0 +1,60 @@
import React, { useState, ChangeEvent } from 'react';
import { Story, Meta } from '@storybook/react';
import { SecretInput, Props } from './SecretInput';
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
export default {
title: 'Forms/SecretInput',
component: SecretInput,
decorators: [withCenteredStory],
parameters: {
controls: {
exclude: [
'prefix',
'suffix',
'addonBefore',
'addonAfter',
'type',
'disabled',
'invalid',
'loading',
'before',
'after',
],
},
},
args: {
width: 50,
placeholder: 'Enter your secret...',
},
argTypes: {
width: { control: { type: 'range', min: 10, max: 200, step: 10 } },
},
} as Meta;
const Template: Story<Props> = (args) => {
const [secret, setSecret] = useState('');
return (
<SecretInput
width={args.width}
value={secret}
isConfigured={args.isConfigured}
placeholder={args.placeholder}
onChange={(event: ChangeEvent<HTMLInputElement>) => setSecret(event.target.value.trim())}
onReset={() => setSecret('')}
/>
);
};
export const basic = Template.bind({});
basic.args = {
isConfigured: false,
};
export const secretIsConfigured = Template.bind({});
secretIsConfigured.args = {
isConfigured: true,
};

@ -0,0 +1,65 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { SecretInput, RESET_BUTTON_TEXT, CONFIGURED_TEXT } from './SecretInput';
const PLACEHOLDER_TEXT = 'Your secret...';
describe('<SecretInput />', () => {
it('should render an input if the secret is not configured', () => {
render(<SecretInput isConfigured={false} onChange={() => {}} onReset={() => {}} placeholder={PLACEHOLDER_TEXT} />);
const input = screen.getByPlaceholderText(PLACEHOLDER_TEXT);
// Should show an enabled input
expect(input).toBeInTheDocument();
expect(input).not.toBeDisabled();
// Should not show a "Reset" button
expect(screen.queryByRole('button', { name: RESET_BUTTON_TEXT })).not.toBeInTheDocument();
});
it('should render a disabled input with a reset button if the secret is already configured', () => {
render(<SecretInput isConfigured={true} onChange={() => {}} onReset={() => {}} placeholder={PLACEHOLDER_TEXT} />);
const input = screen.getByPlaceholderText(PLACEHOLDER_TEXT);
// Should show a disabled input
expect(input).toBeInTheDocument();
expect(input).toBeDisabled();
expect(input).toHaveValue(CONFIGURED_TEXT);
// Should show a reset button
expect(screen.queryByRole('button', { name: RESET_BUTTON_TEXT })).toBeInTheDocument();
});
it('should be possible to reset a configured secret', () => {
const onReset = jest.fn();
render(<SecretInput isConfigured={true} onChange={() => {}} onReset={onReset} placeholder={PLACEHOLDER_TEXT} />);
// Should show a reset button and a disabled input
expect(screen.queryByPlaceholderText(PLACEHOLDER_TEXT)).toBeDisabled();
expect(screen.queryByRole('button', { name: RESET_BUTTON_TEXT })).toBeInTheDocument();
// Click on "Reset"
userEvent.click(screen.getByRole('button', { name: RESET_BUTTON_TEXT }));
expect(onReset).toHaveBeenCalledTimes(1);
});
it('should be possible to change the value of the secret', () => {
const onChange = jest.fn();
render(<SecretInput isConfigured={false} onChange={onChange} onReset={() => {}} placeholder={PLACEHOLDER_TEXT} />);
const input = screen.getByPlaceholderText(PLACEHOLDER_TEXT);
expect(input).toHaveValue('');
userEvent.type(input, 'Foo');
expect(onChange).toHaveBeenCalled();
expect(input).toHaveValue('Foo');
});
});

@ -0,0 +1,26 @@
import * as React from 'react';
import { Input } from '../Input/Input';
import { HorizontalGroup } from '../Layout/Layout';
import { Button } from '../Button';
export type Props = React.ComponentProps<typeof Input> & {
/** TRUE if the secret was already configured. (It is needed as often the backend doesn't send back the actual secret, only the information that it was configured) */
isConfigured: boolean;
/** Called when the user clicks on the "Reset" button in order to clear the secret */
onReset: () => void;
};
export const CONFIGURED_TEXT = 'configured';
export const RESET_BUTTON_TEXT = 'Reset';
export const SecretInput = ({ isConfigured, onReset, ...props }: Props) => (
<HorizontalGroup>
{!isConfigured && <Input {...props} type="password" />}
{isConfigured && <Input {...props} type="text" disabled={true} value={CONFIGURED_TEXT} />}
{isConfigured && (
<Button onClick={onReset} variant="secondary">
{RESET_BUTTON_TEXT}
</Button>
)}
</HorizontalGroup>
);

@ -0,0 +1 @@
export { SecretInput } from './SecretInput';
Loading…
Cancel
Save