mirror of https://github.com/portainer/portainer
feat(ui/buttons): introduce Add and Delete buttons [EE-6296] (#10585)
parent
66635ba6b1
commit
1f2f4525e3
|
@ -1,3 +0,0 @@
|
|||
.add-button {
|
||||
border: none;
|
||||
}
|
|
@ -1,20 +1,21 @@
|
|||
import { Meta, Story } from '@storybook/react';
|
||||
|
||||
import { AddButton, Props } from './AddButton';
|
||||
import { AddButton } from './AddButton';
|
||||
|
||||
export default {
|
||||
component: AddButton,
|
||||
title: 'Components/Buttons/AddButton',
|
||||
} as Meta;
|
||||
|
||||
function Template({ label, onClick }: JSX.IntrinsicAttributes & Props) {
|
||||
return <AddButton label={label} onClick={onClick} />;
|
||||
type Args = {
|
||||
label: string;
|
||||
};
|
||||
|
||||
function Template({ label }: Args) {
|
||||
return <AddButton>{label}</AddButton>;
|
||||
}
|
||||
|
||||
export const Primary: Story<Props> = Template.bind({});
|
||||
export const Primary: Story<Args> = Template.bind({});
|
||||
Primary.args = {
|
||||
label: 'Create new container',
|
||||
onClick: () => {
|
||||
alert('Hello AddButton!');
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,22 +1,18 @@
|
|||
import { fireEvent, render } from '@testing-library/react';
|
||||
import { render } from '@/react-tools/test-utils';
|
||||
|
||||
import { AddButton, Props } from './AddButton';
|
||||
import { AddButton } from './AddButton';
|
||||
|
||||
function renderDefault({
|
||||
label = 'default label',
|
||||
onClick = () => {},
|
||||
}: Partial<Props> = {}) {
|
||||
return render(<AddButton label={label} onClick={onClick} />);
|
||||
}: Partial<{ label: string }> = {}) {
|
||||
return render(<AddButton to="">{label}</AddButton>);
|
||||
}
|
||||
|
||||
test('should display a AddButton component and allow onClick', async () => {
|
||||
test('should display a AddButton component', async () => {
|
||||
const label = 'test label';
|
||||
const onClick = jest.fn();
|
||||
const { findByText } = renderDefault({ label, onClick });
|
||||
|
||||
const { findByText } = renderDefault({ label });
|
||||
|
||||
const buttonLabel = await findByText(label);
|
||||
expect(buttonLabel).toBeTruthy();
|
||||
|
||||
fireEvent.click(buttonLabel);
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
});
|
||||
|
|
|
@ -1,35 +1,38 @@
|
|||
import clsx from 'clsx';
|
||||
import { PlusCircle } from 'lucide-react';
|
||||
import { Plus } from 'lucide-react';
|
||||
import { ComponentProps, PropsWithChildren } from 'react';
|
||||
|
||||
import { Icon } from '@/react/components/Icon';
|
||||
import { AutomationTestingProps } from '@/types';
|
||||
|
||||
import styles from './AddButton.module.css';
|
||||
import { Link } from '@@/Link';
|
||||
|
||||
export interface Props {
|
||||
className?: string;
|
||||
label: string;
|
||||
disabled?: boolean;
|
||||
onClick: () => void;
|
||||
}
|
||||
import { Button } from './Button';
|
||||
|
||||
export function AddButton({ label, onClick, className, disabled }: Props) {
|
||||
export function AddButton({
|
||||
to = '.new',
|
||||
params,
|
||||
children,
|
||||
color = 'primary',
|
||||
disabled,
|
||||
'data-cy': dataCy,
|
||||
}: PropsWithChildren<
|
||||
{
|
||||
to?: string;
|
||||
params?: object;
|
||||
color?: ComponentProps<typeof Button>['color'];
|
||||
disabled?: boolean;
|
||||
} & AutomationTestingProps
|
||||
>) {
|
||||
return (
|
||||
<button
|
||||
className={clsx(
|
||||
className,
|
||||
'label',
|
||||
'label-default',
|
||||
'vertical-center',
|
||||
'interactive',
|
||||
'vertical-center',
|
||||
styles.addButton
|
||||
)}
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
<Button
|
||||
as={Link}
|
||||
props={{ to, params }}
|
||||
icon={Plus}
|
||||
className="!m-0"
|
||||
data-cy={dataCy}
|
||||
color={color}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Icon icon={PlusCircle} />
|
||||
{label}
|
||||
</button>
|
||||
{children || 'Add'}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
import { Trash2 } from 'lucide-react';
|
||||
import { ComponentProps, PropsWithChildren, ReactNode } from 'react';
|
||||
|
||||
import { confirmDelete } from '@@/modals/confirm';
|
||||
|
||||
import { Button } from './Button';
|
||||
|
||||
export function DeleteButton({
|
||||
disabled,
|
||||
confirmMessage,
|
||||
onConfirmed,
|
||||
size,
|
||||
children,
|
||||
}: PropsWithChildren<{
|
||||
size?: ComponentProps<typeof Button>['size'];
|
||||
disabled?: boolean;
|
||||
confirmMessage: ReactNode;
|
||||
onConfirmed(): Promise<void> | void;
|
||||
}>) {
|
||||
return (
|
||||
<Button
|
||||
size={size}
|
||||
color="dangerlight"
|
||||
disabled={disabled}
|
||||
onClick={() => handleClick()}
|
||||
icon={Trash2}
|
||||
className="!m-0"
|
||||
>
|
||||
{children || 'Remove'}
|
||||
</Button>
|
||||
);
|
||||
|
||||
async function handleClick() {
|
||||
if (!(await confirmDelete(confirmMessage))) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return onConfirmed();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
import { useCurrentStateAndParams } from '@uirouter/react';
|
||||
|
||||
import { AutomationTestingProps } from '@/types';
|
||||
|
||||
import { AddButton } from '@@/buttons';
|
||||
|
||||
export function CreateFromManifestButton({
|
||||
params = {},
|
||||
'data-cy': dataCy,
|
||||
}: { params?: object } & AutomationTestingProps) {
|
||||
const { state } = useCurrentStateAndParams();
|
||||
return (
|
||||
<AddButton
|
||||
to="kubernetes.deploy"
|
||||
params={{
|
||||
referrer: state.name,
|
||||
...params,
|
||||
}}
|
||||
data-cy={dataCy}
|
||||
>
|
||||
Create from manifest
|
||||
</AddButton>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue