@snapdragonsnursery/react-components 1.1.38 → 1.3.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
@@ -5,6 +5,11 @@ A collection of reusable React components for Snapdragons Nursery applications.
5
5
  ## Components
6
6
 
7
7
  - **ChildSearchModal**: Advanced child search and selection component with filtering, pagination, and multi-select capabilities
8
+ - **ChildSearchFilters**: Advanced filtering component with date range picker, status, site, and age filters (includes Apply button for better UX)
9
+ - **DateRangePicker**: Shadcn-style date range picker component
10
+ - **DatePicker**: Shadcn-style single date picker component
11
+ - **Calendar**: Official shadcn calendar component
12
+ - **Popover**: Official shadcn popover component
8
13
  - **AuthButtons**: Authentication buttons for MSAL integration
9
14
  - **ThemeToggle**: Dark/light theme toggle component
10
15
  - **LandingPage**: Landing page component with authentication
@@ -34,6 +39,116 @@ function MyComponent() {
34
39
  }
35
40
  ```
36
41
 
42
+ ## Shadcn Components
43
+
44
+ This package includes official shadcn components with proper styling. The components use shadcn CSS variables, so make sure your consuming project has the shadcn CSS variables defined in your CSS file.
45
+
46
+ ### For Consuming Projects
47
+
48
+ 1. **Ensure your project has shadcn CSS variables** in your main CSS file (like `src/index.css`):
49
+
50
+ ```css
51
+ @layer base {
52
+ :root {
53
+ --background: 0 0% 100%;
54
+ --foreground: 222.2 84% 4.9%;
55
+ --primary: 221.2 83.2% 53.3%;
56
+ --primary-foreground: 210 40% 98%;
57
+ --accent: 210 40% 96%;
58
+ --accent-foreground: 222.2 84% 4.9%;
59
+ --muted: 210 40% 96%;
60
+ --muted-foreground: 215.4 16.3% 46.9%;
61
+ --popover: 0 0% 100%;
62
+ --popover-foreground: 222.2 84% 4.9%;
63
+ --border: 214.3 31.8% 91.4%;
64
+ --input: 214.3 31.8% 91.4%;
65
+ --ring: 221.2 83.2% 53.3%;
66
+ --radius: 0.5rem;
67
+ }
68
+ }
69
+ ```
70
+
71
+ 2. **Update your Tailwind config** to include the react-components package:
72
+
73
+ ```js
74
+ // tailwind.config.js
75
+ module.exports = {
76
+ content: [
77
+ './src/**/*.{js,ts,jsx,tsx}',
78
+ './node_modules/@snapdragonsnursery/react-components/src/**/*.{js,jsx}'
79
+ ],
80
+ // ... rest of your config
81
+ }
82
+ ```
83
+
84
+ 3. **Install tailwindcss-animate** for proper animations:
85
+
86
+ ```bash
87
+ npm install tailwindcss-animate
88
+ ```
89
+
90
+ ### Date Range Picker Example
91
+
92
+ ```jsx
93
+ import { DateRangePicker } from '@snapdragonsnursery/react-components';
94
+
95
+ function MyComponent() {
96
+ const [selectedRange, setSelectedRange] = useState(null);
97
+
98
+ const handleDateRangeChange = (range) => {
99
+ // Only update your filters when both dates are selected
100
+ if (range?.from && range?.to) {
101
+ // Both dates selected - update your filters
102
+ setFilters(prev => ({
103
+ ...prev,
104
+ dobFrom: range.from.toISOString().split('T')[0],
105
+ dobTo: range.to.toISOString().split('T')[0],
106
+ }));
107
+ } else if (!range?.from && !range?.to) {
108
+ // Range cleared - clear your filters
109
+ setFilters(prev => ({
110
+ ...prev,
111
+ dobFrom: '',
112
+ dobTo: '',
113
+ }));
114
+ }
115
+ // Don't update filters when only first date is selected
116
+
117
+ setSelectedRange(range);
118
+ };
119
+
120
+ return (
121
+ <DateRangePicker
122
+ selectedRange={selectedRange}
123
+ onSelect={handleDateRangeChange}
124
+ placeholder="Select a date range"
125
+ />
126
+ );
127
+ }
128
+ ```
129
+
130
+ **Important**: The DateRangePicker now maintains its own internal state and only calls `onSelect` when both dates are selected or when the range is cleared. This prevents premature re-renders and keeps the calendar open until the user completes their selection. The ChildSearchFilters component includes an "Apply Filters" button that only triggers searches when clicked, providing better user control.
131
+
132
+ ### Apply Button Functionality
133
+
134
+ The `ChildSearchFilters` component now includes an "Apply Filters" button that provides better user control:
135
+
136
+ - **Local State**: Filter changes are stored locally until applied
137
+ - **Visual Feedback**: Shows "You have unsaved filter changes" when filters are modified
138
+ - **Apply Button**: Only triggers search when clicked
139
+ - **Cancel Button**: Reverts changes without applying
140
+ - **Better UX**: Users can configure multiple filters before searching
141
+
142
+ ```jsx
143
+ <ChildSearchFilters
144
+ filters={filters}
145
+ onFiltersChange={setFilters}
146
+ onApplyFilters={setFilters} // New callback for applying filters
147
+ sites={sites}
148
+ // ... other props
149
+ />
150
+ ```
151
+
37
152
  ## Environment Variables
38
153
 
39
154
  Set these environment variables in your application:
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@snapdragonsnursery/react-components",
3
- "version": "1.1.38",
3
+ "version": "1.3.0",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
7
- "test": "echo \"No tests specified - skipping\"",
7
+ "test": "jest",
8
8
  "version:patch": "npm version patch",
9
9
  "version:minor": "npm version minor",
10
10
  "version:major": "npm version major",
@@ -40,13 +40,19 @@
40
40
  "@headlessui/react": "^2.2.4",
41
41
  "@heroicons/react": "^2.2.0",
42
42
  "@popperjs/core": "^2.11.8",
43
+ "@radix-ui/react-popover": "^1.1.14",
44
+ "@radix-ui/react-slot": "^1.2.3",
43
45
  "@tanstack/react-table": "^8.21.3",
44
46
  "class-variance-authority": "^0.7.1",
45
47
  "clsx": "^2.1.1",
48
+ "date-fns": "^4.1.0",
46
49
  "lucide-react": "^0.526.0",
47
- "react": "^18.3.1",
50
+ "react": "^18.0.0 || ^19.0.0",
51
+ "react-day-picker": "^9.8.1",
48
52
  "react-dom": "^18.3.1",
49
- "tailwind-merge": "^3.3.1"
53
+ "tailwind-merge": "^3.3.1",
54
+ "tailwindcss": "^4.1.11",
55
+ "tailwindcss-animate": "^1.0.7"
50
56
  },
51
57
  "peerDependencies": {
52
58
  "@azure/msal-react": ">=1.0.0",
@@ -54,6 +60,17 @@
54
60
  },
55
61
  "module": "src/index.js",
56
62
  "files": [
57
- "src"
58
- ]
63
+ "src",
64
+ "src/index.css"
65
+ ],
66
+ "devDependencies": {
67
+ "@babel/preset-env": "^7.28.0",
68
+ "@babel/preset-react": "^7.27.1",
69
+ "@testing-library/jest-dom": "^6.6.4",
70
+ "@testing-library/react": "^16.3.0",
71
+ "@testing-library/user-event": "^14.6.1",
72
+ "babel-jest": "^30.0.5",
73
+ "jest": "^30.0.5",
74
+ "jest-environment-jsdom": "^30.0.5"
75
+ }
59
76
  }
@@ -0,0 +1,94 @@
1
+ // Demo component to showcase the Apply button functionality
2
+ // This demonstrates how the ChildSearchFilters component now works with local state and Apply button
3
+
4
+ import React, { useState } from 'react'
5
+ import ChildSearchFilters from './components/ChildSearchFilters'
6
+
7
+ export default function ApplyButtonDemo() {
8
+ const [filters, setFilters] = useState({
9
+ status: "active",
10
+ selectedSiteId: "",
11
+ dobFrom: "",
12
+ dobTo: "",
13
+ ageFrom: "",
14
+ ageTo: "",
15
+ sortBy: "last_name",
16
+ sortOrder: "asc",
17
+ })
18
+
19
+ const [searchTriggered, setSearchTriggered] = useState(false)
20
+
21
+ const mockSites = [
22
+ { site_id: 1, site_name: "Site A" },
23
+ { site_id: 2, site_name: "Site B" },
24
+ ]
25
+
26
+ const handleApplyFilters = (newFilters) => {
27
+ setFilters(newFilters)
28
+ setSearchTriggered(true)
29
+ // In a real app, this would trigger the search
30
+ console.log('Search triggered with filters:', newFilters)
31
+ }
32
+
33
+ const handleClearFilters = () => {
34
+ const clearedFilters = {
35
+ status: "active",
36
+ selectedSiteId: "",
37
+ dobFrom: "",
38
+ dobTo: "",
39
+ ageFrom: "",
40
+ ageTo: "",
41
+ sortBy: "last_name",
42
+ sortOrder: "asc",
43
+ }
44
+ setFilters(clearedFilters)
45
+ setSearchTriggered(false)
46
+ }
47
+
48
+ return (
49
+ <div className="p-6 space-y-6 max-w-4xl mx-auto">
50
+ <h2 className="text-2xl font-bold">Apply Button Demo</h2>
51
+
52
+ <div className="space-y-4">
53
+ <div className="bg-blue-50 dark:bg-blue-900/20 p-4 rounded-lg">
54
+ <h3 className="text-lg font-semibold mb-2">How it works:</h3>
55
+ <ul className="text-sm space-y-1">
56
+ <li>• Change any filter value - notice no search is triggered</li>
57
+ <li>• The "Apply Filters" button appears when you have unsaved changes</li>
58
+ <li>• Click "Apply Filters" to trigger the search</li>
59
+ <li>• Click "Cancel" to revert changes without applying</li>
60
+ </ul>
61
+ </div>
62
+
63
+ <ChildSearchFilters
64
+ filters={filters}
65
+ onFiltersChange={setFilters}
66
+ onApplyFilters={handleApplyFilters}
67
+ sites={mockSites}
68
+ activeOnly={true}
69
+ isAdvancedFiltersOpen={true}
70
+ onToggleAdvancedFilters={() => {}}
71
+ onClearFilters={handleClearFilters}
72
+ />
73
+
74
+ {searchTriggered && (
75
+ <div className="bg-green-50 dark:bg-green-900/20 p-4 rounded-lg">
76
+ <h3 className="text-lg font-semibold text-green-800 dark:text-green-200">
77
+ ✅ Search Triggered!
78
+ </h3>
79
+ <p className="text-sm text-green-700 dark:text-green-300">
80
+ The search was triggered when you clicked "Apply Filters"
81
+ </p>
82
+ </div>
83
+ )}
84
+
85
+ <div className="bg-gray-50 dark:bg-gray-800 p-4 rounded-lg">
86
+ <h3 className="text-lg font-semibold mb-2">Current Filters:</h3>
87
+ <pre className="text-sm overflow-auto">
88
+ {JSON.stringify(filters, null, 2)}
89
+ </pre>
90
+ </div>
91
+ </div>
92
+ </div>
93
+ )
94
+ }
@@ -0,0 +1,39 @@
1
+ // Demo component to test calendar styling
2
+ // This can be used to verify that the shadcn calendar is working properly
3
+
4
+ import React, { useState } from 'react'
5
+ import { Calendar } from './components/ui/calendar'
6
+
7
+ export default function CalendarDemo() {
8
+ const [date, setDate] = useState(null)
9
+
10
+ return (
11
+ <div className="p-6 space-y-4">
12
+ <h2 className="text-2xl font-bold">Calendar Demo</h2>
13
+
14
+ <div className="space-y-2">
15
+ <h3 className="text-lg font-semibold">Single Date Picker</h3>
16
+ <Calendar
17
+ mode="single"
18
+ selected={date}
19
+ onSelect={setDate}
20
+ className="rounded-md border"
21
+ />
22
+ {date && (
23
+ <p className="text-sm text-gray-600">
24
+ Selected: {date.toLocaleDateString()}
25
+ </p>
26
+ )}
27
+ </div>
28
+
29
+ <div className="space-y-2">
30
+ <h3 className="text-lg font-semibold">Date Range Picker</h3>
31
+ <Calendar
32
+ mode="range"
33
+ numberOfMonths={2}
34
+ className="rounded-md border"
35
+ />
36
+ </div>
37
+ </div>
38
+ )
39
+ }