@abstraks-dev/ui-library 2.3.0 → 2.4.0

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.
package/README.md CHANGED
@@ -19,6 +19,7 @@ import {
19
19
  Hero,
20
20
  Alert,
21
21
  Modal,
22
+ Prompt,
22
23
  } from '@abstraks-dev/ui-library';
23
24
  import '@abstraks-dev/ui-library/dist/styles/main.css';
24
25
 
@@ -232,6 +233,51 @@ Props:
232
233
  - `showCloseButton` - Show X close button in header (default: `true`)
233
234
  - `preventBodyScroll` - Lock body scroll when open (default: `true`)
234
235
 
236
+ #### Prompt
237
+
238
+ Lightweight confirmation dialog for user actions like deletes or destructive operations.
239
+
240
+ ```jsx
241
+ import { useState } from 'react';
242
+ import { Prompt, Button } from '@abstraks-dev/ui-library';
243
+
244
+ function MyComponent() {
245
+ const [isOpen, setIsOpen] = useState(false);
246
+
247
+ const handleDelete = () => {
248
+ // Perform delete action
249
+ console.log('Item deleted');
250
+ setIsOpen(false);
251
+ };
252
+
253
+ return (
254
+ <>
255
+ <Button variant='error' onClick={() => setIsOpen(true)}>
256
+ Delete Item
257
+ </Button>
258
+ <Prompt
259
+ message='Are you sure you want to delete this item? This action cannot be undone.'
260
+ open={isOpen}
261
+ onConfirm={handleDelete}
262
+ onCancel={() => setIsOpen(false)}
263
+ confirmText='Delete'
264
+ cancelText='Cancel'
265
+ />
266
+ </>
267
+ );
268
+ }
269
+ ```
270
+
271
+ Props:
272
+
273
+ - `message` **(required)** - The confirmation message to display
274
+ - `open` - Controls prompt visibility (default: `false`)
275
+ - `onConfirm` - Callback when user clicks confirm button
276
+ - `onCancel` - Callback when user clicks cancel button
277
+ - `confirmText` - Text for confirm button (default: `'Yes'`)
278
+ - `cancelText` - Text for cancel button (default: `'Cancel'`)
279
+ - `showCancel` - Whether to show cancel button (default: `true`)
280
+
235
281
  #### Error
236
282
 
237
283
  Error message component.
@@ -35,7 +35,7 @@ describe('Footer', () => {
35
35
  (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Footer.default, {
36
36
  hasAuth: false
37
37
  }));
38
- expect(_react2.screen.getByText(/© 2025 Abstraks. All rights reserved./)).toBeInTheDocument();
38
+ expect(_react2.screen.getByText(/© 2026 Abstraks. All rights reserved./)).toBeInTheDocument();
39
39
  });
40
40
  it('renders auth menu when hasAuth is true on mobile', () => {
41
41
  // Mock mobile screen size so auth menu shows
@@ -110,7 +110,7 @@ describe('Footer', () => {
110
110
  hasAuth: true,
111
111
  authMenu: /*#__PURE__*/_react.default.createElement("li", null, "Menu")
112
112
  }));
113
- expect(_react2.screen.queryByText(/© 2025 Abstraks. All rights reserved./)).not.toBeInTheDocument();
113
+ expect(_react2.screen.queryByText(/© 2026 Abstraks. All rights reserved./)).not.toBeInTheDocument();
114
114
  });
115
115
  it('renders with copyright when hasAuth is true but navigation props are provided', () => {
116
116
  // Mock desktop screen size to test desktop behavior with navigation props
@@ -122,7 +122,7 @@ describe('Footer', () => {
122
122
  authMenu: /*#__PURE__*/_react.default.createElement("li", null, "Menu"),
123
123
  navigationOne: /*#__PURE__*/_react.default.createElement("li", null, "Nav One")
124
124
  }));
125
- expect(_react2.screen.queryByText(/© 2025 Abstraks. All rights reserved./)).toBeInTheDocument();
125
+ expect(_react2.screen.queryByText(/© 2026 Abstraks. All rights reserved./)).toBeInTheDocument();
126
126
  });
127
127
  it('renders with custom className', () => {
128
128
  (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Footer.default, {
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+
3
+ var _react = _interopRequireDefault(require("react"));
4
+ var _react2 = require("@testing-library/react");
5
+ var _Prompt = require("../components/Prompt");
6
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
7
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
8
+ describe('Prompt Component', () => {
9
+ const defaultProps = {
10
+ message: 'Are you sure you want to proceed?',
11
+ onConfirm: jest.fn(),
12
+ onCancel: jest.fn(),
13
+ open: true
14
+ };
15
+ beforeEach(() => {
16
+ jest.clearAllMocks();
17
+ });
18
+ describe('Rendering', () => {
19
+ test('renders prompt when open is true', () => {
20
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, defaultProps));
21
+ expect(_react2.screen.getByRole('dialog')).toBeInTheDocument();
22
+ expect(_react2.screen.getByText('Are you sure you want to proceed?')).toBeInTheDocument();
23
+ });
24
+ test('does not render when open is false', () => {
25
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, _extends({}, defaultProps, {
26
+ open: false
27
+ })));
28
+ expect(_react2.screen.queryByRole('dialog')).not.toBeInTheDocument();
29
+ });
30
+ test('renders with default confirm button text', () => {
31
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, defaultProps));
32
+ expect(_react2.screen.getByText('Yes')).toBeInTheDocument();
33
+ });
34
+ test('renders with custom confirm button text', () => {
35
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, _extends({}, defaultProps, {
36
+ confirmText: "Delete"
37
+ })));
38
+ expect(_react2.screen.getByText('Delete')).toBeInTheDocument();
39
+ expect(_react2.screen.queryByText('Yes')).not.toBeInTheDocument();
40
+ });
41
+ test('renders with default cancel button text', () => {
42
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, defaultProps));
43
+ expect(_react2.screen.getByText('Cancel')).toBeInTheDocument();
44
+ });
45
+ test('renders with custom cancel button text', () => {
46
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, _extends({}, defaultProps, {
47
+ cancelText: "No"
48
+ })));
49
+ expect(_react2.screen.getByText('No')).toBeInTheDocument();
50
+ expect(_react2.screen.queryByText('Cancel')).not.toBeInTheDocument();
51
+ });
52
+ test('renders both confirm and cancel buttons by default', () => {
53
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, defaultProps));
54
+ expect(_react2.screen.getByText('Yes')).toBeInTheDocument();
55
+ expect(_react2.screen.getByText('Cancel')).toBeInTheDocument();
56
+ });
57
+ test('hides cancel button when showCancel is false', () => {
58
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, _extends({}, defaultProps, {
59
+ showCancel: false
60
+ })));
61
+ expect(_react2.screen.getByText('Yes')).toBeInTheDocument();
62
+ expect(_react2.screen.queryByText('Cancel')).not.toBeInTheDocument();
63
+ });
64
+ test('renders without confirm button if onConfirm is not provided', () => {
65
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, {
66
+ message: "Information message",
67
+ onCancel: defaultProps.onCancel,
68
+ open: true
69
+ }));
70
+ expect(_react2.screen.queryByText('Yes')).not.toBeInTheDocument();
71
+ expect(_react2.screen.getByText('Cancel')).toBeInTheDocument();
72
+ });
73
+ test('has correct ARIA attributes', () => {
74
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, defaultProps));
75
+ const dialog = _react2.screen.getByRole('dialog');
76
+ expect(dialog).toHaveAttribute('aria-modal', 'true');
77
+ expect(dialog).toHaveAttribute('aria-label', 'Are you sure you want to proceed?');
78
+ });
79
+ test('confirm button receives focus when rendered', () => {
80
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, defaultProps));
81
+ const confirmButton = _react2.screen.getByText('Yes');
82
+ expect(confirmButton).toHaveFocus();
83
+ });
84
+ });
85
+ describe('User Interactions', () => {
86
+ test('calls onConfirm when confirm button is clicked', () => {
87
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, defaultProps));
88
+ const confirmButton = _react2.screen.getByText('Yes');
89
+ _react2.fireEvent.click(confirmButton);
90
+ expect(defaultProps.onConfirm).toHaveBeenCalledTimes(1);
91
+ expect(defaultProps.onCancel).not.toHaveBeenCalled();
92
+ });
93
+ test('calls onCancel when cancel button is clicked', () => {
94
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, defaultProps));
95
+ const cancelButton = _react2.screen.getByText('Cancel');
96
+ _react2.fireEvent.click(cancelButton);
97
+ expect(defaultProps.onCancel).toHaveBeenCalledTimes(1);
98
+ expect(defaultProps.onConfirm).not.toHaveBeenCalled();
99
+ });
100
+ test('does not call onConfirm if not provided', () => {
101
+ const mockOnCancel = jest.fn();
102
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, {
103
+ message: "Test message",
104
+ onCancel: mockOnCancel,
105
+ open: true,
106
+ showCancel: true
107
+ }));
108
+
109
+ // Only cancel button should be present
110
+ expect(_react2.screen.queryByText('Yes')).not.toBeInTheDocument();
111
+ });
112
+ test('handles multiple clicks correctly', () => {
113
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, defaultProps));
114
+ const confirmButton = _react2.screen.getByText('Yes');
115
+ _react2.fireEvent.click(confirmButton);
116
+ _react2.fireEvent.click(confirmButton);
117
+ expect(defaultProps.onConfirm).toHaveBeenCalledTimes(2);
118
+ });
119
+ });
120
+ describe('PropTypes Validation', () => {
121
+ test('renders with required props only', () => {
122
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, {
123
+ message: "Required message",
124
+ open: true
125
+ }));
126
+ expect(_react2.screen.getByRole('dialog')).toBeInTheDocument();
127
+ expect(_react2.screen.getByText('Required message')).toBeInTheDocument();
128
+ });
129
+ test('renders with all optional props', () => {
130
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, {
131
+ message: "All props message",
132
+ onConfirm: jest.fn(),
133
+ onCancel: jest.fn(),
134
+ confirmText: "Confirm",
135
+ cancelText: "Dismiss",
136
+ showCancel: true,
137
+ open: true
138
+ }));
139
+ expect(_react2.screen.getByText('All props message')).toBeInTheDocument();
140
+ expect(_react2.screen.getByText('Confirm')).toBeInTheDocument();
141
+ expect(_react2.screen.getByText('Dismiss')).toBeInTheDocument();
142
+ });
143
+ });
144
+ describe('CSS Classes', () => {
145
+ test('applies correct CSS classes', () => {
146
+ const {
147
+ container
148
+ } = (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, defaultProps));
149
+ expect(container.querySelector('.prompt-overlay')).toBeInTheDocument();
150
+ expect(container.querySelector('.prompt-box')).toBeInTheDocument();
151
+ expect(container.querySelector('.prompt-actions')).toBeInTheDocument();
152
+ });
153
+ test('confirm button has correct class', () => {
154
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, defaultProps));
155
+ const confirmButton = _react2.screen.getByText('Yes');
156
+ expect(confirmButton).toHaveClass('prompt-confirm');
157
+ });
158
+ test('cancel button has correct class', () => {
159
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, defaultProps));
160
+ const cancelButton = _react2.screen.getByText('Cancel');
161
+ expect(cancelButton).toHaveClass('prompt-cancel');
162
+ });
163
+ });
164
+ describe('Edge Cases', () => {
165
+ test('renders with empty message string', () => {
166
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, _extends({}, defaultProps, {
167
+ message: ""
168
+ })));
169
+ expect(_react2.screen.getByRole('dialog')).toBeInTheDocument();
170
+ });
171
+ test('renders with very long message', () => {
172
+ const longMessage = 'This is a very long message that might wrap to multiple lines in the prompt dialog and should still render correctly without breaking the layout or causing any issues';
173
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, _extends({}, defaultProps, {
174
+ message: longMessage
175
+ })));
176
+ expect(_react2.screen.getByText(longMessage)).toBeInTheDocument();
177
+ });
178
+ test('renders when both onConfirm and onCancel are undefined', () => {
179
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, {
180
+ message: "Message",
181
+ open: true
182
+ }));
183
+ expect(_react2.screen.getByRole('dialog')).toBeInTheDocument();
184
+ expect(_react2.screen.getByText('Message')).toBeInTheDocument();
185
+ });
186
+ test('transitions from closed to open', () => {
187
+ const {
188
+ rerender
189
+ } = (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, _extends({}, defaultProps, {
190
+ open: false
191
+ })));
192
+ expect(_react2.screen.queryByRole('dialog')).not.toBeInTheDocument();
193
+ rerender(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, _extends({}, defaultProps, {
194
+ open: true
195
+ })));
196
+ expect(_react2.screen.getByRole('dialog')).toBeInTheDocument();
197
+ });
198
+ test('transitions from open to closed', () => {
199
+ const {
200
+ rerender
201
+ } = (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, _extends({}, defaultProps, {
202
+ open: true
203
+ })));
204
+ expect(_react2.screen.getByRole('dialog')).toBeInTheDocument();
205
+ rerender(/*#__PURE__*/_react.default.createElement(_Prompt.Prompt, _extends({}, defaultProps, {
206
+ open: false
207
+ })));
208
+ expect(_react2.screen.queryByRole('dialog')).not.toBeInTheDocument();
209
+ });
210
+ });
211
+ });
package/dist/index.js CHANGED
@@ -26,6 +26,7 @@ var _exportNames = {
26
26
  Loader: true,
27
27
  Modal: true,
28
28
  Paragraph: true,
29
+ Prompt: true,
29
30
  Radio: true,
30
31
  Search: true,
31
32
  Select: true,
@@ -380,6 +381,12 @@ Object.defineProperty(exports, "PlusCircle", {
380
381
  return _PlusCircle.PlusCircle;
381
382
  }
382
383
  });
384
+ Object.defineProperty(exports, "Prompt", {
385
+ enumerable: true,
386
+ get: function () {
387
+ return _Prompt.default;
388
+ }
389
+ });
383
390
  Object.defineProperty(exports, "Radio", {
384
391
  enumerable: true,
385
392
  get: function () {
@@ -492,6 +499,7 @@ var _Label = _interopRequireDefault(require("./components/Label"));
492
499
  var _Loader = _interopRequireDefault(require("./components/Loader"));
493
500
  var _Modal = _interopRequireDefault(require("./components/Modal"));
494
501
  var _Paragraph = _interopRequireDefault(require("./components/Paragraph"));
502
+ var _Prompt = _interopRequireDefault(require("./components/Prompt"));
495
503
  var _Radio = require("./components/Radio");
496
504
  var _Search = require("./components/Search");
497
505
  var _Select = require("./components/Select");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abstraks-dev/ui-library",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "User Interface library",
5
5
  "main": "dist/index.js",
6
6
  "files": [