@strato-admin/cloudscape 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -2,6 +2,8 @@ export interface BulkDeleteButtonProps {
|
|
|
2
2
|
label?: string;
|
|
3
3
|
variant?: 'primary' | 'normal' | 'link';
|
|
4
4
|
mutationMode?: 'undoable' | 'optimistic' | 'pessimistic';
|
|
5
|
+
dialogTitle?: string;
|
|
6
|
+
dialogDescription?: string;
|
|
5
7
|
}
|
|
6
|
-
export declare const BulkDeleteButton: ({ label, variant, mutationMode, }: BulkDeleteButtonProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
8
|
+
export declare const BulkDeleteButton: ({ label, variant, mutationMode, dialogTitle, dialogDescription, }: BulkDeleteButtonProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
7
9
|
export default BulkDeleteButton;
|
|
@@ -1,17 +1,43 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { useBulkDeleteController, useTranslate, useListContext, useResourceDefinition, useResourceContext, useGetResourceLabel, } from '@strato-admin/core';
|
|
4
|
+
import Modal from '@cloudscape-design/components/modal';
|
|
5
|
+
import Box from '@cloudscape-design/components/box';
|
|
6
|
+
import SpaceBetween from '@cloudscape-design/components/space-between';
|
|
3
7
|
import { Button } from './Button';
|
|
4
|
-
export const BulkDeleteButton = ({ label, variant = 'normal', mutationMode = 'pessimistic', }) => {
|
|
8
|
+
export const BulkDeleteButton = ({ label, variant = 'normal', mutationMode = 'pessimistic', dialogTitle, dialogDescription, }) => {
|
|
5
9
|
const translate = useTranslate();
|
|
6
10
|
const { selectedIds } = useListContext();
|
|
11
|
+
const resource = useResourceContext();
|
|
12
|
+
const getResourceLabel = useGetResourceLabel();
|
|
7
13
|
const { options } = useResourceDefinition();
|
|
8
14
|
const { handleDelete, isPending, isLoading } = useBulkDeleteController({
|
|
9
15
|
mutationMode,
|
|
10
16
|
});
|
|
17
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
11
18
|
if (options?.canDelete === false) {
|
|
12
19
|
return null;
|
|
13
20
|
}
|
|
14
21
|
const isBusy = isPending || isLoading;
|
|
15
|
-
|
|
22
|
+
const handleConfirm = () => {
|
|
23
|
+
handleDelete();
|
|
24
|
+
setIsOpen(false);
|
|
25
|
+
};
|
|
26
|
+
const handleOpen = () => {
|
|
27
|
+
setIsOpen(true);
|
|
28
|
+
};
|
|
29
|
+
const handleClose = () => {
|
|
30
|
+
setIsOpen(false);
|
|
31
|
+
};
|
|
32
|
+
console.log('selectedIds', selectedIds);
|
|
33
|
+
const defaultTitle = translate('ra.message.bulk_delete_title', {
|
|
34
|
+
smart_count: selectedIds?.length || 0,
|
|
35
|
+
_: `Delete ${selectedIds?.length || 0} items`,
|
|
36
|
+
});
|
|
37
|
+
const defaultDescription = translate('ra.message.bulk_delete_content', {
|
|
38
|
+
smart_count: selectedIds?.length || 0,
|
|
39
|
+
_: `Are you sure you want to delete these ${selectedIds?.length || 0} items?`,
|
|
40
|
+
});
|
|
41
|
+
return (_jsxs(_Fragment, { children: [_jsx(Button, { variant: variant, onClick: handleOpen, loading: isBusy, disabled: isBusy || !selectedIds || selectedIds.length === 0, children: label || translate('ra.action.delete', { _: 'Delete' }) }), _jsx(Modal, { onDismiss: handleClose, visible: isOpen, closeAriaLabel: translate('ra.action.close', { _: 'Close' }), footer: _jsx(Box, { float: "right", children: _jsxs(SpaceBetween, { direction: "horizontal", size: "xs", children: [_jsx(Button, { variant: "link", onClick: handleClose, children: translate('ra.action.cancel', { _: 'Cancel' }) }), _jsx(Button, { variant: "primary", onClick: handleConfirm, loading: isBusy, "data-testid": "confirm-bulk-delete", children: translate('ra.action.confirm', { _: 'Confirm' }) })] }) }), header: dialogTitle || defaultTitle, children: dialogDescription || defaultDescription })] }));
|
|
16
42
|
};
|
|
17
43
|
export default BulkDeleteButton;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strato-admin/cloudscape",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Strato Admin Cloudscape implementation - UI component library and theme for React Admin",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"repository": {
|
|
33
33
|
"type": "git",
|
|
34
|
-
"url": "https://github.com/
|
|
34
|
+
"url": "https://github.com/vadimgu/strato-admin.git",
|
|
35
35
|
"directory": "packages/strato-cloudscape"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
"inflection": "^3.0.2",
|
|
42
42
|
"react-hook-form": "^7.71.2",
|
|
43
43
|
"react-router-dom": "^6.22.3",
|
|
44
|
-
"@strato-admin/
|
|
45
|
-
"@strato-admin/
|
|
46
|
-
"@strato-admin/
|
|
44
|
+
"@strato-admin/language-en": "0.1.1",
|
|
45
|
+
"@strato-admin/core": "0.1.1",
|
|
46
|
+
"@strato-admin/i18n": "0.1.1"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@chromatic-com/storybook": "^5.0.1",
|
|
@@ -2,13 +2,22 @@
|
|
|
2
2
|
import { render, screen, fireEvent, cleanup } from '@testing-library/react';
|
|
3
3
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
4
4
|
import { BulkDeleteButton } from './BulkDeleteButton';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
useListContext,
|
|
7
|
+
useBulkDeleteController,
|
|
8
|
+
useTranslate,
|
|
9
|
+
useResourceDefinition,
|
|
10
|
+
useResourceContext,
|
|
11
|
+
useGetResourceLabel,
|
|
12
|
+
} from '@strato-admin/core';
|
|
6
13
|
|
|
7
14
|
vi.mock('@strato-admin/core', () => ({
|
|
8
15
|
useListContext: vi.fn(),
|
|
9
16
|
useBulkDeleteController: vi.fn(),
|
|
10
17
|
useTranslate: vi.fn(),
|
|
11
18
|
useResourceDefinition: vi.fn(),
|
|
19
|
+
useResourceContext: vi.fn(),
|
|
20
|
+
useGetResourceLabel: vi.fn(),
|
|
12
21
|
}));
|
|
13
22
|
|
|
14
23
|
describe('BulkDeleteButton', () => {
|
|
@@ -21,6 +30,8 @@ describe('BulkDeleteButton', () => {
|
|
|
21
30
|
isLoading: false,
|
|
22
31
|
});
|
|
23
32
|
(useResourceDefinition as any).mockReturnValue({ options: {} });
|
|
33
|
+
(useResourceContext as any).mockReturnValue('posts');
|
|
34
|
+
(useGetResourceLabel as any).mockReturnValue(() => 'Posts');
|
|
24
35
|
});
|
|
25
36
|
|
|
26
37
|
afterEach(() => {
|
|
@@ -30,13 +41,12 @@ describe('BulkDeleteButton', () => {
|
|
|
30
41
|
it('should be disabled when no ids are selected', () => {
|
|
31
42
|
(useListContext as any).mockReturnValue({ selectedIds: [] });
|
|
32
43
|
render(<BulkDeleteButton />);
|
|
33
|
-
expect(screen.
|
|
44
|
+
expect(screen.getByText('Delete').closest('button')).toHaveProperty('disabled', true);
|
|
34
45
|
});
|
|
35
46
|
|
|
36
47
|
it('should render when ids are selected', () => {
|
|
37
48
|
(useListContext as any).mockReturnValue({ selectedIds: [1, 2] });
|
|
38
49
|
render(<BulkDeleteButton />);
|
|
39
|
-
expect(screen.getByRole('button')).toBeDefined();
|
|
40
50
|
expect(screen.getByText('Delete')).toBeDefined();
|
|
41
51
|
});
|
|
42
52
|
|
|
@@ -47,7 +57,7 @@ describe('BulkDeleteButton', () => {
|
|
|
47
57
|
expect(container.firstChild).toBeNull();
|
|
48
58
|
});
|
|
49
59
|
|
|
50
|
-
it('should call handleDelete on
|
|
60
|
+
it('should open dialog on click and call handleDelete on confirm', () => {
|
|
51
61
|
const handleDelete = vi.fn();
|
|
52
62
|
(useBulkDeleteController as any).mockReturnValue({
|
|
53
63
|
handleDelete,
|
|
@@ -57,8 +67,22 @@ describe('BulkDeleteButton', () => {
|
|
|
57
67
|
(useListContext as any).mockReturnValue({ selectedIds: [1, 2] });
|
|
58
68
|
|
|
59
69
|
render(<BulkDeleteButton />);
|
|
60
|
-
fireEvent.click(screen.
|
|
70
|
+
fireEvent.click(screen.getByText('Delete'));
|
|
61
71
|
|
|
72
|
+
expect(handleDelete).not.toHaveBeenCalled();
|
|
73
|
+
expect(screen.getByText('Delete 2 items')).toBeDefined();
|
|
74
|
+
expect(screen.getByText('Are you sure you want to delete these 2 items?')).toBeDefined();
|
|
75
|
+
|
|
76
|
+
fireEvent.click(screen.getByTestId('confirm-bulk-delete'));
|
|
62
77
|
expect(handleDelete).toHaveBeenCalled();
|
|
63
78
|
});
|
|
79
|
+
|
|
80
|
+
it('should use custom dialogTitle and dialogDescription', () => {
|
|
81
|
+
(useListContext as any).mockReturnValue({ selectedIds: [1, 2] });
|
|
82
|
+
render(<BulkDeleteButton dialogTitle="Custom Title" dialogDescription="Custom Description" />);
|
|
83
|
+
fireEvent.click(screen.getByText('Delete'));
|
|
84
|
+
|
|
85
|
+
expect(screen.getByText('Custom Title')).toBeDefined();
|
|
86
|
+
expect(screen.getByText('Custom Description')).toBeDefined();
|
|
87
|
+
});
|
|
64
88
|
});
|
|
@@ -1,40 +1,108 @@
|
|
|
1
1
|
|
|
2
|
-
import
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
|
+
import {
|
|
4
|
+
useBulkDeleteController,
|
|
5
|
+
useTranslate,
|
|
6
|
+
useListContext,
|
|
7
|
+
useResourceDefinition,
|
|
8
|
+
useResourceContext,
|
|
9
|
+
useGetResourceLabel,
|
|
10
|
+
} from '@strato-admin/core';
|
|
11
|
+
import Modal from '@cloudscape-design/components/modal';
|
|
12
|
+
import Box from '@cloudscape-design/components/box';
|
|
13
|
+
import SpaceBetween from '@cloudscape-design/components/space-between';
|
|
3
14
|
import { Button } from './Button';
|
|
4
15
|
|
|
5
16
|
export interface BulkDeleteButtonProps {
|
|
6
17
|
label?: string;
|
|
7
18
|
variant?: 'primary' | 'normal' | 'link';
|
|
8
19
|
mutationMode?: 'undoable' | 'optimistic' | 'pessimistic';
|
|
20
|
+
dialogTitle?: string;
|
|
21
|
+
dialogDescription?: string;
|
|
9
22
|
}
|
|
10
23
|
|
|
11
24
|
export const BulkDeleteButton = ({
|
|
12
25
|
label,
|
|
13
26
|
variant = 'normal',
|
|
14
27
|
mutationMode = 'pessimistic',
|
|
28
|
+
dialogTitle,
|
|
29
|
+
dialogDescription,
|
|
15
30
|
}: BulkDeleteButtonProps) => {
|
|
16
31
|
const translate = useTranslate();
|
|
17
32
|
const { selectedIds } = useListContext();
|
|
33
|
+
const resource = useResourceContext();
|
|
34
|
+
const getResourceLabel = useGetResourceLabel();
|
|
18
35
|
const { options } = useResourceDefinition();
|
|
19
36
|
const { handleDelete, isPending, isLoading } = useBulkDeleteController({
|
|
20
37
|
mutationMode,
|
|
21
38
|
});
|
|
22
39
|
|
|
40
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
41
|
+
|
|
23
42
|
if (options?.canDelete === false) {
|
|
24
43
|
return null;
|
|
25
44
|
}
|
|
26
45
|
|
|
27
46
|
const isBusy = isPending || isLoading;
|
|
28
47
|
|
|
48
|
+
const handleConfirm = () => {
|
|
49
|
+
handleDelete();
|
|
50
|
+
setIsOpen(false);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const handleOpen = () => {
|
|
54
|
+
setIsOpen(true);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const handleClose = () => {
|
|
58
|
+
setIsOpen(false);
|
|
59
|
+
};
|
|
60
|
+
console.log('selectedIds', selectedIds);
|
|
61
|
+
const defaultTitle = translate('ra.message.bulk_delete_title', {
|
|
62
|
+
smart_count: selectedIds?.length || 0,
|
|
63
|
+
_: `Delete ${selectedIds?.length || 0} items`,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const defaultDescription = translate('ra.message.bulk_delete_content', {
|
|
67
|
+
smart_count: selectedIds?.length || 0,
|
|
68
|
+
_: `Are you sure you want to delete these ${selectedIds?.length || 0} items?`,
|
|
69
|
+
});
|
|
29
70
|
return (
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
71
|
+
<>
|
|
72
|
+
<Button
|
|
73
|
+
variant={variant}
|
|
74
|
+
onClick={handleOpen}
|
|
75
|
+
loading={isBusy}
|
|
76
|
+
disabled={isBusy || !selectedIds || selectedIds.length === 0}
|
|
77
|
+
>
|
|
78
|
+
{label || translate('ra.action.delete', { _: 'Delete' })}
|
|
79
|
+
</Button>
|
|
80
|
+
<Modal
|
|
81
|
+
onDismiss={handleClose}
|
|
82
|
+
visible={isOpen}
|
|
83
|
+
closeAriaLabel={translate('ra.action.close', { _: 'Close' })}
|
|
84
|
+
footer={
|
|
85
|
+
<Box float="right">
|
|
86
|
+
<SpaceBetween direction="horizontal" size="xs">
|
|
87
|
+
<Button variant="link" onClick={handleClose}>
|
|
88
|
+
{translate('ra.action.cancel', { _: 'Cancel' })}
|
|
89
|
+
</Button>
|
|
90
|
+
<Button
|
|
91
|
+
variant="primary"
|
|
92
|
+
onClick={handleConfirm}
|
|
93
|
+
loading={isBusy}
|
|
94
|
+
data-testid="confirm-bulk-delete"
|
|
95
|
+
>
|
|
96
|
+
{translate('ra.action.confirm', { _: 'Confirm' })}
|
|
97
|
+
</Button>
|
|
98
|
+
</SpaceBetween>
|
|
99
|
+
</Box>
|
|
100
|
+
}
|
|
101
|
+
header={dialogTitle || defaultTitle}
|
|
102
|
+
>
|
|
103
|
+
{dialogDescription || defaultDescription}
|
|
104
|
+
</Modal>
|
|
105
|
+
</>
|
|
38
106
|
);
|
|
39
107
|
};
|
|
40
108
|
|