1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-21 22:39:41 +02:00

feat(ui/buttons): introduce Add and Delete buttons [EE-6296] (#10585)

This commit is contained in:
Chaim Lev-Ari 2023-11-14 12:36:15 +02:00 committed by GitHub
parent 66635ba6b1
commit 1f2f4525e3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 108 additions and 47 deletions

View file

@ -1,3 +0,0 @@
.add-button {
border: none;
}

View file

@ -1,20 +1,21 @@
import { Meta, Story } from '@storybook/react'; import { Meta, Story } from '@storybook/react';
import { AddButton, Props } from './AddButton'; import { AddButton } from './AddButton';
export default { export default {
component: AddButton, component: AddButton,
title: 'Components/Buttons/AddButton', title: 'Components/Buttons/AddButton',
} as Meta; } as Meta;
function Template({ label, onClick }: JSX.IntrinsicAttributes & Props) { type Args = {
return <AddButton label={label} onClick={onClick} />; 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 = { Primary.args = {
label: 'Create new container', label: 'Create new container',
onClick: () => {
alert('Hello AddButton!');
},
}; };

View file

@ -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({ function renderDefault({
label = 'default label', label = 'default label',
onClick = () => {}, }: Partial<{ label: string }> = {}) {
}: Partial<Props> = {}) { return render(<AddButton to="">{label}</AddButton>);
return render(<AddButton label={label} onClick={onClick} />);
} }
test('should display a AddButton component and allow onClick', async () => { test('should display a AddButton component', async () => {
const label = 'test label'; const label = 'test label';
const onClick = jest.fn();
const { findByText } = renderDefault({ label, onClick }); const { findByText } = renderDefault({ label });
const buttonLabel = await findByText(label); const buttonLabel = await findByText(label);
expect(buttonLabel).toBeTruthy(); expect(buttonLabel).toBeTruthy();
fireEvent.click(buttonLabel);
expect(onClick).toHaveBeenCalled();
}); });

View file

@ -1,35 +1,38 @@
import clsx from 'clsx'; import { Plus } from 'lucide-react';
import { PlusCircle } 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 { import { Button } from './Button';
className?: string;
label: string;
disabled?: boolean;
onClick: () => void;
}
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 ( return (
<button <Button
className={clsx( as={Link}
className, props={{ to, params }}
'label', icon={Plus}
'label-default', className="!m-0"
'vertical-center', data-cy={dataCy}
'interactive', color={color}
'vertical-center',
styles.addButton
)}
type="button"
onClick={onClick}
disabled={disabled} disabled={disabled}
> >
<Icon icon={PlusCircle} /> {children || 'Add'}
{label} </Button>
</button>
); );
} }

View file

@ -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();
}
}

View file

@ -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>
);
}