jattac.libs.web.zest-button 1.4.0 → 1.5.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.
@@ -0,0 +1,289 @@
1
+ # The Cookbook: From First Button to Design System
2
+
3
+ Welcome to the ZestButton Cookbook! This is the core learning path for mastering `ZestButton`. Each recipe solves a real-world problem and builds on the concepts from the previous one. Start here to go from zero to expert.
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ - [Recipe 1: Your First Async Button](#recipe-1-your-first-async-button)
10
+ - [Recipe 2: The Safe "Delete" Button](#recipe-2-the-safe-delete-button)
11
+ - [Recipe 3: Standardizing Your Buttons with a Global Config](#recipe-3-standardizing-your-buttons-with-a-global-config)
12
+ - [Recipe 4: Creating a Custom "Archive" Button](#recipe-4-creating-a-custom-archive-button)
13
+ - [Recipe 5: A Split Button with Overflow Actions](#recipe-5-a-split-button-with-overflow-actions)
14
+
15
+ ---
16
+
17
+ ### Recipe 1: Your First Async Button
18
+
19
+ **Goal:** Create a button that automatically shows a loading spinner during an operation and gives feedback when it's done.
20
+
21
+ **Problem:** You have an API call that takes time. You need to prevent the user from clicking the button multiple times and clearly show when the action is complete.
22
+
23
+ **Solution:** Simply have your `onClick` handler return a `Promise`. `ZestButton` handles the rest. This example also shows success and failure states.
24
+
25
+ ```tsx
26
+ import React, { useState } from 'react';
27
+ import ZestButton from 'jattac.libs.web.zest-button';
28
+ import { FaSave } from 'react-icons/fa';
29
+
30
+ const SaveButton = () => {
31
+ const [shouldSucceed, setShouldSucceed] = useState(true);
32
+
33
+ const handleSave = async () => {
34
+ console.log('Saving...');
35
+ // Simulate an API call
36
+ await new Promise((resolve, reject) => {
37
+ setTimeout(() => {
38
+ shouldSucceed ? resolve('Success!') : reject('Error!');
39
+ }, 1500);
40
+ });
41
+ };
42
+
43
+ return (
44
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem', maxWidth: '300px' }}>
45
+ <label>
46
+ <input
47
+ type="checkbox"
48
+ checked={shouldSucceed}
49
+ onChange={() => setShouldSucceed(e => !e)}
50
+ />
51
+ Simulate Success
52
+ </label>
53
+ <ZestButton
54
+ onClick={handleSave}
55
+ zest={{
56
+ visualOptions: { iconLeft: <FaSave />, stretch: true },
57
+ }}
58
+ >
59
+ Save Settings
60
+ </ZestButton>
61
+ </div>
62
+ );
63
+ };
64
+ ```
65
+ *For more details on all available options, see the [`BusyOptions`](./api.md#busyoptions) and [`SuccessOptions`](./api.md#successoptions) in our API reference.*
66
+
67
+ ---
68
+
69
+ ### Recipe 2: The Safe "Delete" Button
70
+
71
+ **Goal:** Create a button for a destructive action that requires a second click to confirm.
72
+
73
+ **Problem:** Destructive actions like deleting data are dangerous. A user might click the button by accident.
74
+
75
+ **Solution:** Use the `confirmOptions` prop. This forces the user to click once to start a countdown, and a second time to execute the action. Combining this with a `danger` variant provides a clear visual warning.
76
+
77
+ ```tsx
78
+ import React from 'react';
79
+ import ZestButton from 'jattac.libs.web.zest-button';
80
+ import { FaTrash } from 'react-icons/fa';
81
+
82
+ const DeleteButton = () => {
83
+ const handleDelete = () => {
84
+ alert('Item has been permanently deleted.');
85
+ };
86
+
87
+ return (
88
+ <ZestButton
89
+ onClick={handleDelete}
90
+ zest={{
91
+ visualOptions: {
92
+ variant: 'danger',
93
+ iconLeft: <FaTrash />,
94
+ },
95
+ confirmOptions: {
96
+ displayLabel: 'Confirm Deletion',
97
+ timeoutSecs: 5,
98
+ },
99
+ }}
100
+ >
101
+ Delete Account
102
+ </ZestButton>
103
+ );
104
+ };
105
+ ```
106
+ *For more details, see the [`ConfirmOptions`](./api.md#confirmoptions) in our API reference.*
107
+
108
+ ---
109
+
110
+ ### Recipe 3: Standardizing Your Buttons with a Global Config
111
+
112
+ **Goal:** Define a consistent look and feel for all buttons in your application without repeating props.
113
+
114
+ **Problem:** Your app has dozens of buttons. Specifying `size: 'sm'` or `buttonStyle: 'outline'` on every single one is tedious and error-prone.
115
+
116
+ **Solution:** Wrap your application in the `ZestProvider` and pass a `config` object. Any props in `defaultProps` will be applied to every `ZestButton` within the provider. Local props on a button will always override the global default.
117
+
118
+ ```tsx
119
+ // In your main App.tsx
120
+ import React from 'react';
121
+ import { ZestButtonConfigProvider, ZestButton } from 'jattac.libs.web.zest-button';
122
+
123
+ const appZestConfig = {
124
+ defaultProps: {
125
+ visualOptions: {
126
+ size: 'sm', // Make all buttons small by default
127
+ },
128
+ buttonStyle: 'outline', // Make all buttons outline by default
129
+ },
130
+ };
131
+
132
+ const App = () => (
133
+ <ZestButtonConfigProvider config={appZestConfig}>
134
+ <div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
135
+ <ZestButton>I'm a small outline button</ZestButton>
136
+ <ZestButton>So am I</ZestButton>
137
+ <ZestButton zest={{ visualOptions: { size: 'lg' }, buttonStyle: 'solid' }}>
138
+ I'm a large solid button! (Local override)
139
+ </ZestButton>
140
+ </div>
141
+ </ZestButtonConfigProvider>
142
+ );
143
+ ```
144
+ *For a full list of provider settings, see the [`ZestGlobalConfig`](./api.md#zestglobalconfig) documentation. To understand the precedence rules, see the [Configuration Guide](./configuration.md).*
145
+
146
+ ---
147
+
148
+ ### Recipe 4: Creating a Custom "Archive" Button
149
+
150
+ **Goal:** Create a new, reusable button "type" with its own specific icon, style, and behavior that can be used anywhere in the app.
151
+
152
+ **Problem:** You have a common action in your app, like "Archive," that should always look and feel the same. You want to avoid configuring it manually each time and just be able to write `zest={{ semanticType: 'archive' }}`.
153
+
154
+ **Solution:** This is a two-step process that combines **TypeScript Module Augmentation** with the **`ZestButtonConfigProvider`**.
155
+
156
+ **Step 1: Define the new type**
157
+ In your project's type declarations file (e.g., `src/zest.d.ts`), augment the `CustomZestSemanticTypes` interface.
158
+
159
+ ```typescript
160
+ // src/zest.d.ts
161
+ import 'jattac.libs.web.zest-button';
162
+ import { FaArchive } from 'react-icons/fa';
163
+
164
+ // 1. Tell ZestButton that 'archive' is a valid semantic type
165
+ declare module 'jattac.libs.web.zest-button' {
166
+ export interface CustomZestSemanticTypes {
167
+ archive: 'archive';
168
+ }
169
+ }
170
+ ```
171
+ *(For more on this, see the [Contributor's Guide](./development.md#extending-semantic-types).)*
172
+
173
+ **Step 2: Provide the default configuration**
174
+ In your `App.tsx`, use the `semanticTypeDefaults` property in the `ZestButtonConfigProvider` to define the default props for your new `'archive'` type.
175
+
176
+ ```tsx
177
+ // In your main App.tsx
178
+ import React from 'react';
179
+ import { ZestButtonConfigProvider, ZestButton } from 'jattac.libs.web.zest-button';
180
+ import { FaArchive } from 'react-icons/fa';
181
+
182
+ const appZestConfig = {
183
+ semanticTypeDefaults: {
184
+ // 2. Define the default props for the 'archive' type
185
+ archive: {
186
+ buttonStyle: 'outline',
187
+ visualOptions: {
188
+ iconLeft: <FaArchive />,
189
+ variant: 'standard',
190
+ },
191
+ confirmOptions: {
192
+ displayLabel: 'Confirm Archive?',
193
+ timeoutSecs: 10,
194
+ },
195
+ },
196
+ // You can also override built-in types here!
197
+ delete: {
198
+ buttonStyle: 'outline', // Make all delete buttons 'outline'
199
+ }
200
+ },
201
+ };
202
+
203
+ const App = () => (
204
+ <ZestButtonConfigProvider config={appZestConfig}>
205
+ <div style={{ display: 'flex', gap: '1rem' }}>
206
+ {/* 3. Now just use it! */}
207
+ <ZestButton
208
+ zest={{ semanticType: 'archive' }}
209
+ onClick={() => alert('Archived!')}
210
+ >
211
+ Archive
212
+ </ZestButton>
213
+
214
+ <ZestButton
215
+ zest={{ semanticType: 'delete' }}
216
+ onClick={() => alert('Deleted!')}
217
+ >
218
+ Delete
219
+ </ZestButton>
220
+ </div>
221
+ </ZestButtonConfigProvider>
222
+ );
223
+ ```
224
+ This powerful pattern allows you to build a complete, consistent design system for all button actions in your application. For more details on configuration, see the [Configuration Guide](./configuration.md).*
225
+
226
+ ---
227
+
228
+ ### Recipe 5: A Split Button with Overflow Actions
229
+
230
+ **Goal:** Create a button with one obvious default action ("Export as CSV") plus a few situational alternatives ("Export as PDF", "Export as JSON"), without cluttering the UI with three separate buttons.
231
+
232
+ **Problem:** You have a primary action and a handful of related, less-common actions. Placing them all as top-level buttons crowds the UI; hiding all of them behind a menu makes the primary action less discoverable and adds an extra click for the common case.
233
+
234
+ **Solution:** Pass `dropdownOptions` in `zest`. `ZestButton` renders as two visually-fused segments: the main action (left) fires immediately on click, exactly like a normal `ZestButton`, while the chevron (right) opens a menu of the extra options. Each option is independent — it can have its own icon, `busyOptions`, `successOptions`, or `confirmOptions`, running through the same machinery as the main button.
235
+
236
+ ```tsx
237
+ import React from 'react';
238
+ import ZestButton from 'jattac.libs.web.zest-button';
239
+ import { FaFileCsv, FaFilePdf, FaFileCode, FaTrash } from 'react-icons/fa6';
240
+
241
+ const ExportButton = () => {
242
+ const exportAs = (format: string) => async () => {
243
+ await new Promise((resolve) => setTimeout(resolve, 800));
244
+ alert(`Exported as ${format}!`);
245
+ };
246
+
247
+ return (
248
+ <ZestButton
249
+ onClick={exportAs('CSV')}
250
+ zest={{
251
+ visualOptions: { iconLeft: <FaFileCsv /> },
252
+ dropdownOptions: [
253
+ { label: 'Export as PDF', icon: <FaFilePdf />, onClick: exportAs('PDF') },
254
+ { label: 'Export as JSON', icon: <FaFileCode />, onClick: exportAs('JSON') },
255
+ {
256
+ label: 'Delete export history',
257
+ icon: <FaTrash />,
258
+ onClick: async () => alert('History cleared.'),
259
+ confirmOptions: { displayLabel: 'Confirm Delete', timeoutSecs: 5 },
260
+ },
261
+ ],
262
+ }}
263
+ >
264
+ Export as CSV
265
+ </ZestButton>
266
+ );
267
+ };
268
+ ```
269
+
270
+ **Theming and sizing the menu independently:** the dropdown menu panel defaults to a light theme and a minimum width matching the whole split button, regardless of the main button's own `theme`. Both are configurable, either per-button or app-wide via `ZestButtonConfigProvider`'s `defaultProps`:
271
+
272
+ ```tsx
273
+ <ZestButton
274
+ zest={{
275
+ theme: 'dark',
276
+ dropdownTheme: 'dark', // pin the menu's theme, instead of following the OS/browser preference
277
+ dropdownWidth: '16rem', // force a specific minimum width instead of matching the button
278
+ dropdownOptions: [{ label: 'Export as PDF', onClick: exportAs('PDF') }],
279
+ }}
280
+ >
281
+ Export as CSV
282
+ </ZestButton>
283
+ ```
284
+
285
+ *For the full prop reference, see [`dropdownOptions`](./api.md#zestcustomprops) and [`ZestDropdownOption`](./api.md#zestdropdownoption) in our API reference.*
286
+
287
+ ---
288
+
289
+ [⬅️ Previous: README](../README.md) | [Next: Features Showcase ➡️](./features.md)
@@ -0,0 +1,150 @@
1
+ # Features: A Guided Tour
2
+
3
+ This document provides a high-level showcase of what's possible with `ZestButton`. For detailed, practical implementation guides, please see our **[Cookbook](./examples.md)**.
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ - [Asynchronous Operations & Feedback](#asynchronous-operations--feedback)
10
+ - [Confirmation Flow](#confirmation-flow)
11
+ - [Semantic Types](#semantic-types)
12
+ - [Global Configuration](#global-configuration)
13
+ - [Rich Styling](#rich-styling)
14
+ - [Split Buttons & Overflow Menus](#split-buttons--overflow-menus)
15
+
16
+ ---
17
+
18
+ ### Asynchronous Operations & Feedback
19
+
20
+ **What it does:** Automatically handles loading states and provides clear success/failure feedback for any action that returns a Promise.
21
+
22
+ ```tsx
23
+ const MyComponent = () => {
24
+ const handleAsyncClick = async () => {
25
+ // Simulate an API call that takes 2 seconds
26
+ await new Promise(resolve => setTimeout(resolve, 2000));
27
+ };
28
+
29
+ return (
30
+ <ZestButton onClick={handleAsyncClick}>
31
+ Perform Async Action
32
+ </ZestButton>
33
+ );
34
+ };
35
+ ```
36
+ *__Learn more in the [Your First Async Button recipe](./examples.md#recipe-1-your-first-async-button).__*
37
+
38
+ ---
39
+
40
+ ### Confirmation Flow
41
+
42
+ **What it does:** Protects users from accidentally performing critical actions by requiring a second click to confirm.
43
+
44
+ ```tsx
45
+ const MyComponent = () => {
46
+ const handleDelete = () => alert('The item has been deleted!');
47
+
48
+ return (
49
+ <ZestButton
50
+ onClick={handleDelete}
51
+ zest={{
52
+ confirmOptions: { displayLabel: 'Confirm?', timeoutSecs: 5 },
53
+ visualOptions: { variant: 'danger' }
54
+ }}
55
+ >
56
+ Delete Item
57
+ </ZestButton>
58
+ );
59
+ };
60
+ ```
61
+ *__Learn more in the [Safe "Delete" Button recipe](./examples.md#recipe-2-the-safe-delete-button).__*
62
+
63
+ ---
64
+
65
+ ### Semantic Types
66
+
67
+ **What it does:** Streamlines development by allowing you to declare button intent (e.g., `'save'`, `'delete'`). The button automatically gets appropriate icons, colors, and behaviors.
68
+
69
+ ```tsx
70
+ const MyComponent = () => (
71
+ <div style={{ display: 'flex', gap: '1rem' }}>
72
+ <ZestButton zest={{ semanticType: 'save' }}>Save</ZestButton>
73
+ <ZestButton zest={{ semanticType: 'cancel' }}>Cancel</ZestButton>
74
+ </div>
75
+ );
76
+ ```
77
+ *__Learn how to create your own in the [Creating a Custom "Archive" Button recipe](./examples.md#recipe-4-creating-a-custom-archive-button).__*
78
+
79
+ ---
80
+
81
+ ### Global Configuration
82
+
83
+ **What it does:** Allows you to define a consistent style (like size or button style) for all buttons across your entire application.
84
+
85
+ ```tsx
86
+ // In your App.tsx
87
+ const appZestConfig = {
88
+ defaultProps: {
89
+ visualOptions: { size: 'sm' },
90
+ buttonStyle: 'outline',
91
+ },
92
+ };
93
+
94
+ const App = () => (
95
+ <ZestButtonConfigProvider config={appZestConfig}>
96
+ {/* All buttons inside will be small and outline by default */}
97
+ </ZestButtonConfigProvider>
98
+ );
99
+ ```
100
+ *__Learn more in the [Standardizing Your Buttons recipe](./examples.md#recipe-3-standardizing-your-buttons-with-a-global-config).__*
101
+
102
+ ---
103
+
104
+ ### Rich Styling
105
+
106
+ **What it does:** Provides multiple variants, sizes, and styles (`solid`, `outline`, `text`, `dashed`) to fit any UI context. It also automatically adapts to light and dark themes.
107
+
108
+ ```tsx
109
+ const MyComponent = () => (
110
+ <div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
111
+ <ZestButton zest={{ visualOptions: { variant: 'success', size: 'sm' } }}>
112
+ Small Success
113
+ </ZestButton>
114
+ <ZestButton zest={{ buttonStyle: 'outline', size: 'md' }}>
115
+ Medium Outline
116
+ </ZestButton>
117
+ <ZestButton zest={{ visualOptions: { variant: 'danger', size: 'lg' } }}>
118
+ Large Danger
119
+ </ZestButton>
120
+ </div>
121
+ );
122
+ ```
123
+ *Explore all the recipes in the **[Cookbook](./examples.md)** to see these options in action.*
124
+
125
+ ---
126
+
127
+ ### Split Buttons & Overflow Menus
128
+
129
+ **What it does:** Attaches a set of secondary actions to a button that already has one obvious default action, via a chevron segment that opens a menu — a discoverable place for "there's more here" without cluttering the UI with extra buttons. Each menu item gets its own independent busy/success/fail/confirm feedback, and the menu can be themed and sized independently of the main button.
130
+
131
+ ```tsx
132
+ const MyComponent = () => (
133
+ <ZestButton
134
+ onClick={() => alert('Exported as CSV!')}
135
+ zest={{
136
+ dropdownOptions: [
137
+ { label: 'Export as PDF', onClick: () => alert('Exported as PDF!') },
138
+ { label: 'Export as JSON', onClick: () => alert('Exported as JSON!') },
139
+ ],
140
+ }}
141
+ >
142
+ Export as CSV
143
+ </ZestButton>
144
+ );
145
+ ```
146
+ *__Learn more in the [Split Button with Overflow Actions recipe](./examples.md#recipe-5-a-split-button-with-overflow-actions).__*
147
+
148
+ ---
149
+
150
+ [⬅️ Previous: The Cookbook (Examples)](./examples.md) | [Next: API Reference ➡️](./api.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jattac.libs.web.zest-button",
3
- "version": "1.4.0",
3
+ "version": "1.5.1",
4
4
  "description": "A highly customizable and production-ready React button component featuring robust asynchronous handling, rich visual feedback, and built-in confirmation flows for enhanced user experience",
5
5
  "homepage": "https://github.com/nyingimaina/jattac.libs.web.zest-button#readme",
6
6
  "repository": {
@@ -16,7 +16,8 @@
16
16
  "files": [
17
17
  "dist",
18
18
  "README.md",
19
- "docs"
19
+ "LICENSE",
20
+ "documentation"
20
21
  ],
21
22
  "scripts": {
22
23
  "build": "rollup -c rollup.config.mjs",
@@ -1,75 +0,0 @@
1
- # Critical Paths
2
-
3
- This document defines the critical business paths that MUST be covered by e2E tests.
4
-
5
- ---
6
-
7
- ## How to Identify Critical Paths
8
-
9
- A path is critical if:
10
-
11
- • It handles money or payments
12
-
13
- • It manages user authentication or authorization
14
-
15
- • It writes to the database in ways that affect business data
16
-
17
- • It integrates with external systems
18
-
19
- • If broken, it causes immediate user-facing impact
20
-
21
- ---
22
-
23
- ## Paths
24
-
25
- ### Authentication
26
-
27
- - [ ] Login with valid credentials → 200 + token
28
-
29
- - [ ] Login with invalid credentials → 401
30
-
31
- - [ ] Token refresh → 200 + new token
32
-
33
- - [ ] Logout → 200 + token invalidated
34
-
35
- ### Payment
36
-
37
- - [ ] Create payment → 201 + payment record
38
-
39
- - [ ] Confirm payment → 200 + status updated
40
-
41
- - [ ] Handle payment failure → 200 + status updated
42
-
43
- - [ ] Process refund → 200 + refund record
44
-
45
- ### Order
46
-
47
- - [ ] Create order → 201 + order record
48
-
49
- - [ ] Pay order → 200 + order status updated
50
-
51
- - [ ] Ship order → 200 + order status updated
52
-
53
- - [ ] Complete order → 200 + order status updated
54
-
55
- - [ ] Cancel order → 200 + order status updated
56
-
57
- ### Data
58
-
59
- - [ ] Import data → 200 + records created
60
-
61
- - [ ] Export data → 200 + file returned
62
-
63
- ---
64
-
65
- ## Adding New Paths
66
-
67
- When adding a new critical path:
68
-
69
- 1. Add the path description above
70
-
71
- 2. List each scenario with expected outcome
72
-
73
- 3. Write e2e tests covering each scenario
74
-
75
- 4. Mark scenarios as tested: `- [x]`