UI: ConfirmButton component

This commit is contained in:
Alexander Zobnin
2019-12-10 13:28:48 +03:00
parent f24b84faef
commit d99e2fc074
4 changed files with 256 additions and 0 deletions
@@ -0,0 +1,73 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { text, boolean, select } from '@storybook/addon-knobs';
import { ConfirmButton } from './ConfirmButton';
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
import { action } from '@storybook/addon-actions';
import { Button } from '../Button/Button';
const getKnobs = () => {
return {
buttonText: text('Button text', 'Edit'),
confirmText: text('Confirm text', 'Save'),
confirmVariant: select(
'Confirm variant',
{
primary: 'primary',
secondary: 'secondary',
danger: 'danger',
inverse: 'inverse',
transparent: 'transparent',
},
'primary'
),
disabled: boolean('Disabled', false),
};
};
storiesOf('UI/ConfirmButton', module)
.addDecorator(withCenteredStory)
.add('default', () => {
const { buttonText, confirmText, confirmVariant, disabled } = getKnobs();
return (
<>
<div className="gf-form-group">
<div className="gf-form">
<ConfirmButton
confirmText={confirmText}
disabled={disabled}
confirmButtonVariant={confirmVariant}
onConfirm={() => {
action('Saved')('save!');
}}
>
{buttonText}
</ConfirmButton>
</div>
</div>
</>
);
})
.add('with custom button', () => {
const { buttonText, confirmText, confirmVariant, disabled } = getKnobs();
return (
<>
<div className="gf-form-group">
<div className="gf-form">
<ConfirmButton
confirmText={confirmText}
disabled={disabled}
confirmButtonVariant={confirmVariant}
onConfirm={() => {
action('Saved')('save!');
}}
>
<Button size="sm" variant="secondary" icon="fa fa-pencil">
{buttonText}
</Button>
</ConfirmButton>
</div>
</div>
</>
);
});
@@ -0,0 +1,34 @@
import React from 'react';
import { ConfirmButton } from './ConfirmButton';
import { mount, ShallowWrapper } from 'enzyme';
import { Button } from '../Button/Button';
describe('ConfirmButton', () => {
let wrapper: any;
let deleted: any;
beforeAll(() => {
deleted = false;
function deleteItem() {
deleted = true;
}
wrapper = mount(
<ConfirmButton confirmText="Confirm delete" onConfirm={() => deleteItem()}>
Delete
</ConfirmButton>
);
});
it('should show confirm delete when clicked', () => {
expect(deleted).toBe(false);
wrapper
.find(Button)
.findWhere((n: ShallowWrapper) => {
return n.text() === 'Confirm delete' && n.type() === Button;
})
.simulate('click');
expect(deleted).toBe(true);
});
});
@@ -0,0 +1,148 @@
import React, { PureComponent, SyntheticEvent } from 'react';
import { cx, css } from 'emotion';
import { stylesFactory, withTheme } from '../../themes';
import { GrafanaTheme } from '@grafana/data';
import { Themeable } from '../../types';
import { Button } from '../Button/Button';
import { ButtonVariant } from '../Button/types';
const getStyles = stylesFactory((theme: GrafanaTheme) => {
return {
buttonContainer: css`
direction: rtl;
display: flex;
align-items: center;
`,
buttonDisabled: css`
text-decoration: none;
color: ${theme.colors.text};
opacity: 0.65;
cursor: not-allowed;
pointer-events: none;
`,
buttonShow: css`
opacity: 1;
transition: opacity 0.1s ease;
z-index: 2;
`,
buttonHide: css`
opacity: 0;
transition: opacity 0.1s ease;
z-index: 0;
`,
confirmButtonContainer: css`
overflow: hidden;
position: absolute;
z-index: 1;
`,
confirmButton: css`
display: flex;
align-items: flex-start;
`,
confirmButtonShow: css`
opacity: 1;
transition: opacity 0.08s ease-out, transform 0.1s ease-out;
transform: translateX(0);
`,
confirmButtonHide: css`
opacity: 0;
transition: opacity 0.12s ease-in, transform 0.14s ease-in;
transform: translateX(100px);
`,
};
});
interface Props extends Themeable {
className?: string;
confirmText?: string;
disabled?: boolean;
confirmButtonVariant?: ButtonVariant;
onConfirm(): void;
onClick?(): void;
onCancel?(): void;
}
interface State {
showConfirm: boolean;
}
class UnThemedConfirmButton extends PureComponent<Props, State> {
static defaultProps: Partial<Props> = {
confirmText: 'Save',
disabled: false,
confirmButtonVariant: 'primary',
};
state: State = {
showConfirm: false,
};
onClickButton = (event: SyntheticEvent) => {
if (event) {
event.preventDefault();
}
this.setState({
showConfirm: true,
});
if (this.props.onClick) {
this.props.onClick();
}
};
onClickCancel = (event: SyntheticEvent) => {
if (event) {
event.preventDefault();
}
this.setState({
showConfirm: false,
});
if (this.props.onCancel) {
this.props.onCancel();
}
};
render() {
const { onConfirm, disabled, confirmText, confirmButtonVariant, className, theme, children } = this.props;
const styles = getStyles(theme);
const buttonClass = cx(
className,
this.state.showConfirm ? styles.buttonHide : styles.buttonShow,
disabled && styles.buttonDisabled
);
const confirmButtonClass = cx(
styles.confirmButton,
this.state.showConfirm ? styles.confirmButtonShow : styles.confirmButtonHide
);
const onClick = disabled ? () => {} : this.onClickButton;
return (
<span className={styles.buttonContainer}>
{typeof children === 'string' ? (
<Button className={buttonClass} size="sm" variant="link" onClick={onClick}>
{children}
</Button>
) : (
<span className={buttonClass} onClick={onClick}>
{children}
</span>
)}
<span className={styles.confirmButtonContainer}>
<span className={confirmButtonClass}>
<Button size="sm" variant="transparent" onClick={this.onClickCancel}>
Cancel
</Button>
<Button size="sm" variant={confirmButtonVariant} onClick={onConfirm}>
{confirmText}
</Button>
</span>
</span>
</span>
);
}
}
export const ConfirmButton = withTheme(UnThemedConfirmButton);
ConfirmButton.displayName = 'ConfirmButton';
@@ -1,4 +1,5 @@
export { DeleteButton } from './DeleteButton/DeleteButton';
export { ConfirmButton } from './ConfirmButton/ConfirmButton';
export { Tooltip, PopoverContent } from './Tooltip/Tooltip';
export { PopoverController } from './Tooltip/PopoverController';
export { Popover } from './Tooltip/Popover';