@visns-studio/visns-components 5.8.20 → 5.9.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 +149 -0
- package/package.json +1 -1
- package/src/components/crm/DataGrid.jsx +288 -45
- package/src/components/crm/Field.jsx +2 -2
- package/src/components/crm/MultiSelect.jsx +33 -13
- package/src/components/crm/styles/DataGrid.module.scss +16 -6
- package/src/components/crm/styles/MultiSelect.module.scss +31 -9
- package/src/components/crm/styles/global-datagrid.css +16 -10
- package/src/examples/MultiSelectStylingTest.jsx +76 -0
package/README.md
CHANGED
|
@@ -1375,6 +1375,155 @@ We welcome contributions! Please follow these steps:
|
|
|
1375
1375
|
|
|
1376
1376
|
[MIT License](LICENSE)
|
|
1377
1377
|
|
|
1378
|
+
## Modal Actions in Components
|
|
1379
|
+
|
|
1380
|
+
### Using Modal Actions in GenericQuote Component
|
|
1381
|
+
|
|
1382
|
+
Modal actions allow you to open a form modal when a user clicks an action button. This is useful for collecting additional information from users or submitting data related to the quote.
|
|
1383
|
+
|
|
1384
|
+
#### Implementation Methods
|
|
1385
|
+
|
|
1386
|
+
There are two ways to implement this functionality:
|
|
1387
|
+
|
|
1388
|
+
**1. Using the "addModal" type:**
|
|
1389
|
+
|
|
1390
|
+
```javascript
|
|
1391
|
+
{
|
|
1392
|
+
"id": "anyCustomId1",
|
|
1393
|
+
"label": "Submit Form",
|
|
1394
|
+
"type": "addModal",
|
|
1395
|
+
"setting": {
|
|
1396
|
+
// Form settings go here (see structure below)
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
```
|
|
1400
|
+
|
|
1401
|
+
**2. Using the "modal" type (alternative approach):**
|
|
1402
|
+
|
|
1403
|
+
```javascript
|
|
1404
|
+
{
|
|
1405
|
+
"id": "anyCustomId2",
|
|
1406
|
+
"label": "Submit Form",
|
|
1407
|
+
"type": "modal",
|
|
1408
|
+
"setting": {
|
|
1409
|
+
// Form settings go here (see structure below)
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
```
|
|
1413
|
+
|
|
1414
|
+
Both approaches are treated identically in the code.
|
|
1415
|
+
|
|
1416
|
+
#### Form Settings Structure
|
|
1417
|
+
|
|
1418
|
+
The `setting` property MUST follow this specific structure:
|
|
1419
|
+
|
|
1420
|
+
```javascript
|
|
1421
|
+
"setting": {
|
|
1422
|
+
// IMPORTANT: The 'create' property is required for the form to work properly
|
|
1423
|
+
"create": {
|
|
1424
|
+
"title": "Submit New Form",
|
|
1425
|
+
"submitUrl": "/api/forms/submit",
|
|
1426
|
+
"method": "POST"
|
|
1427
|
+
},
|
|
1428
|
+
// The 'fields' array is required to define the form fields
|
|
1429
|
+
"fields": [
|
|
1430
|
+
{
|
|
1431
|
+
"id": "name",
|
|
1432
|
+
"label": "Name",
|
|
1433
|
+
"type": "text",
|
|
1434
|
+
"required": "yes",
|
|
1435
|
+
"size": "half"
|
|
1436
|
+
},
|
|
1437
|
+
{
|
|
1438
|
+
"id": "email",
|
|
1439
|
+
"label": "Email",
|
|
1440
|
+
"type": "email",
|
|
1441
|
+
"required": "yes",
|
|
1442
|
+
"size": "half"
|
|
1443
|
+
},
|
|
1444
|
+
{
|
|
1445
|
+
"id": "message",
|
|
1446
|
+
"label": "Message",
|
|
1447
|
+
"type": "textarea",
|
|
1448
|
+
"required": "yes",
|
|
1449
|
+
"size": "full"
|
|
1450
|
+
}
|
|
1451
|
+
]
|
|
1452
|
+
}
|
|
1453
|
+
```
|
|
1454
|
+
|
|
1455
|
+
#### Required Form Settings Properties
|
|
1456
|
+
|
|
1457
|
+
- `create`: Object containing settings for the create form (REQUIRED)
|
|
1458
|
+
|
|
1459
|
+
- `title`: The title displayed at the top of the modal
|
|
1460
|
+
- `submitUrl`: The URL to submit the form data to
|
|
1461
|
+
- `method`: The HTTP method to use (POST, PUT, etc.)
|
|
1462
|
+
|
|
1463
|
+
- `fields`: Array of field objects that define the form fields (REQUIRED)
|
|
1464
|
+
- `id`: Unique identifier for the field
|
|
1465
|
+
- `label`: Display label for the field
|
|
1466
|
+
- `type`: Field type (text, email, textarea, select, etc.)
|
|
1467
|
+
- `required`: Whether the field is required ("yes" or "no")
|
|
1468
|
+
- `size`: Field size in the form layout ("full", "half", "quarter")
|
|
1469
|
+
- Additional properties depending on the field type
|
|
1470
|
+
|
|
1471
|
+
## Troubleshooting
|
|
1472
|
+
|
|
1473
|
+
### DataGrid Checkbox Issues
|
|
1474
|
+
|
|
1475
|
+
#### Checkbox Cell Click Bug Fix
|
|
1476
|
+
|
|
1477
|
+
**Problem:** Clicking anywhere within a checkbox cell (but outside the actual checkbox input) incorrectly cleared all other selected checkboxes, leaving only the clicked row selected.
|
|
1478
|
+
|
|
1479
|
+
**Solution:** Enhanced click detection to distinguish between checkbox input clicks and cell area clicks. The fix ensures that both checkbox input clicks and cell area clicks behave identically - toggling only the specific row while maintaining all other selections.
|
|
1480
|
+
|
|
1481
|
+
**Files Modified:**
|
|
1482
|
+
|
|
1483
|
+
- `src/components/crm/DataGrid.jsx` - Enhanced click handling logic
|
|
1484
|
+
- `src/components/crm/styles/global-datagrid.css` - Improved cell styling
|
|
1485
|
+
|
|
1486
|
+
#### Checkbox Improvements
|
|
1487
|
+
|
|
1488
|
+
The DataGrid checkbox functionality has been enhanced with:
|
|
1489
|
+
|
|
1490
|
+
- Larger touch targets for better mobile usability
|
|
1491
|
+
- Consistent behavior between input and cell area clicks
|
|
1492
|
+
- Proper selection state management
|
|
1493
|
+
- Improved visual feedback
|
|
1494
|
+
|
|
1495
|
+
### Modal Action Troubleshooting
|
|
1496
|
+
|
|
1497
|
+
If the modal doesn't open when clicking the action button, check the following:
|
|
1498
|
+
|
|
1499
|
+
1. Make sure the action object has either:
|
|
1500
|
+
|
|
1501
|
+
- `id: "addModal"` or
|
|
1502
|
+
- `type: "modal"`
|
|
1503
|
+
|
|
1504
|
+
2. Check the browser console for any errors or debug messages
|
|
1505
|
+
|
|
1506
|
+
3. **IMPORTANT**: Verify that the `setting` property has the REQUIRED structure:
|
|
1507
|
+
|
|
1508
|
+
- It MUST have a `create` object containing at least `title`, `submitUrl`, and `method`
|
|
1509
|
+
- It MUST have a `fields` array with properly defined field objects
|
|
1510
|
+
|
|
1511
|
+
4. If using the default form settings (without a custom `setting` property), make sure the component has valid form settings in its props.
|
|
1512
|
+
|
|
1513
|
+
## Development Notes
|
|
1514
|
+
|
|
1515
|
+
### DataGrid Layout Optimization
|
|
1516
|
+
|
|
1517
|
+
The DataGrid component has been optimized for vertical space by conditionally relocating the auto refresh control when it's the sole UI control present. This provides better space utilization and cleaner interfaces.
|
|
1518
|
+
|
|
1519
|
+
### Modal Alignment Implementation
|
|
1520
|
+
|
|
1521
|
+
Modal components support vertical alignment configuration with the `verticalAlign` property. Setting `verticalAlign: 'top'` positions modals at 5vh from the top of the viewport for better user experience.
|
|
1522
|
+
|
|
1523
|
+
### Grouping Investigation Results
|
|
1524
|
+
|
|
1525
|
+
The ReactDataGrid's `defaultGroupBy` prop enables internal grouping functionality that creates collapsible group headers within the table structure. It does NOT create any external UI elements that need relocation, as all grouping controls are internal to the table component.
|
|
1526
|
+
|
|
1378
1527
|
## Support
|
|
1379
1528
|
|
|
1380
1529
|
For support, please contact the VISNS Studio team or open an issue on GitHub.
|
package/package.json
CHANGED
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
87
87
|
},
|
|
88
88
|
"name": "@visns-studio/visns-components",
|
|
89
|
-
"version": "5.
|
|
89
|
+
"version": "5.9.0",
|
|
90
90
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
91
91
|
"main": "src/index.js",
|
|
92
92
|
"files": [
|
|
@@ -43,6 +43,7 @@ import { toast } from 'react-toastify';
|
|
|
43
43
|
import imageCompression from 'browser-image-compression';
|
|
44
44
|
import Vapor from 'laravel-vapor';
|
|
45
45
|
import Lightbox from 'yet-another-react-lightbox';
|
|
46
|
+
import { Tooltip } from 'react-tooltip';
|
|
46
47
|
import fetchUtil from '../../utils/fetchUtil';
|
|
47
48
|
import { confirmDialog } from '../utils/ConfirmDialog';
|
|
48
49
|
import {
|
|
@@ -146,6 +147,123 @@ const loadData = async (
|
|
|
146
147
|
}
|
|
147
148
|
};
|
|
148
149
|
|
|
150
|
+
// Utility function to check if text content is truncated
|
|
151
|
+
const isTextTruncated = (element) => {
|
|
152
|
+
if (!element) return false;
|
|
153
|
+
return (
|
|
154
|
+
element.scrollWidth > element.clientWidth ||
|
|
155
|
+
element.scrollHeight > element.clientHeight
|
|
156
|
+
);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// Utility function to format cell value for tooltip display
|
|
160
|
+
const formatCellValueForTooltip = (value, columnType) => {
|
|
161
|
+
if (value === null || value === undefined || value === '') {
|
|
162
|
+
return '';
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
switch (columnType) {
|
|
166
|
+
case 'date':
|
|
167
|
+
case 'datetime':
|
|
168
|
+
if (
|
|
169
|
+
moment(value).isValid() &&
|
|
170
|
+
moment(value).format('YYYY-MM-DD') !== '1970-01-01'
|
|
171
|
+
) {
|
|
172
|
+
return moment(value).format('DD-MM-YYYY HH:mm');
|
|
173
|
+
}
|
|
174
|
+
return String(value);
|
|
175
|
+
case 'currency':
|
|
176
|
+
return numeral(value).format('$0,0.00');
|
|
177
|
+
case 'number':
|
|
178
|
+
return numeral(value).format('0,0');
|
|
179
|
+
case 'boolean':
|
|
180
|
+
return value ? 'Yes' : 'No';
|
|
181
|
+
case 'richtext':
|
|
182
|
+
// Strip HTML tags for tooltip
|
|
183
|
+
const tempDiv = document.createElement('div');
|
|
184
|
+
tempDiv.innerHTML = value;
|
|
185
|
+
return tempDiv.textContent || tempDiv.innerText || '';
|
|
186
|
+
default:
|
|
187
|
+
return String(value);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// Component to wrap cell content with tooltip functionality
|
|
192
|
+
const CellWithTooltip = ({
|
|
193
|
+
children,
|
|
194
|
+
value,
|
|
195
|
+
columnType = 'text',
|
|
196
|
+
className = '',
|
|
197
|
+
}) => {
|
|
198
|
+
const cellRef = useRef(null);
|
|
199
|
+
const [showTooltip, setShowTooltip] = useState(false);
|
|
200
|
+
const [tooltipId] = useState(
|
|
201
|
+
() => `cell-tooltip-${Math.random().toString(36).substr(2, 9)}`
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
const checkTruncation = () => {
|
|
206
|
+
if (cellRef.current) {
|
|
207
|
+
const isTruncated = isTextTruncated(cellRef.current);
|
|
208
|
+
setShowTooltip(isTruncated);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// Check truncation on mount and when content changes
|
|
213
|
+
checkTruncation();
|
|
214
|
+
|
|
215
|
+
// Also check on window resize
|
|
216
|
+
const handleResize = () => checkTruncation();
|
|
217
|
+
window.addEventListener('resize', handleResize);
|
|
218
|
+
|
|
219
|
+
return () => {
|
|
220
|
+
window.removeEventListener('resize', handleResize);
|
|
221
|
+
};
|
|
222
|
+
}, [children, value]);
|
|
223
|
+
|
|
224
|
+
const tooltipContent = formatCellValueForTooltip(value, columnType);
|
|
225
|
+
|
|
226
|
+
// Only show tooltip if content is truncated and we have meaningful content
|
|
227
|
+
const shouldShowTooltip =
|
|
228
|
+
showTooltip && tooltipContent && tooltipContent.trim() !== '';
|
|
229
|
+
|
|
230
|
+
return (
|
|
231
|
+
<div
|
|
232
|
+
ref={cellRef}
|
|
233
|
+
className={className}
|
|
234
|
+
style={{
|
|
235
|
+
overflow: 'hidden',
|
|
236
|
+
textOverflow: 'ellipsis',
|
|
237
|
+
whiteSpace: 'nowrap',
|
|
238
|
+
width: '100%',
|
|
239
|
+
}}
|
|
240
|
+
{...(shouldShowTooltip && {
|
|
241
|
+
'data-tooltip-id': tooltipId,
|
|
242
|
+
'data-tooltip-content': tooltipContent,
|
|
243
|
+
})}
|
|
244
|
+
>
|
|
245
|
+
{children}
|
|
246
|
+
{shouldShowTooltip && (
|
|
247
|
+
<Tooltip
|
|
248
|
+
id={tooltipId}
|
|
249
|
+
style={{
|
|
250
|
+
backgroundColor: 'rgba(0, 0, 0, 0.9)',
|
|
251
|
+
color: 'white',
|
|
252
|
+
borderRadius: '6px',
|
|
253
|
+
padding: '8px 12px',
|
|
254
|
+
fontSize: '13px',
|
|
255
|
+
maxWidth: '300px',
|
|
256
|
+
wordWrap: 'break-word',
|
|
257
|
+
zIndex: 9999,
|
|
258
|
+
}}
|
|
259
|
+
place="top"
|
|
260
|
+
offset={5}
|
|
261
|
+
/>
|
|
262
|
+
)}
|
|
263
|
+
</div>
|
|
264
|
+
);
|
|
265
|
+
};
|
|
266
|
+
|
|
149
267
|
const DataGrid = forwardRef(
|
|
150
268
|
(
|
|
151
269
|
{
|
|
@@ -378,6 +496,18 @@ const DataGrid = forwardRef(
|
|
|
378
496
|
}
|
|
379
497
|
|
|
380
498
|
sqlResult.then((res) => {
|
|
499
|
+
// Sort data by grouping field if grouping is enabled
|
|
500
|
+
if (ajaxSetting?.groupBy?.length > 0) {
|
|
501
|
+
const groupField = ajaxSetting.groupBy[0]; // groupBy is array of strings
|
|
502
|
+
res.data.sort((a, b) => {
|
|
503
|
+
const aValue = a[groupField] || '';
|
|
504
|
+
const bValue = b[groupField] || '';
|
|
505
|
+
return aValue
|
|
506
|
+
.toString()
|
|
507
|
+
.localeCompare(bValue.toString());
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
|
|
381
511
|
if (setTotal) {
|
|
382
512
|
setTotal(res.count);
|
|
383
513
|
}
|
|
@@ -1538,10 +1668,11 @@ const DataGrid = forwardRef(
|
|
|
1538
1668
|
'var(--light-bg-color, #f5f5f5)',
|
|
1539
1669
|
borderRadius: '4px',
|
|
1540
1670
|
padding: '4px',
|
|
1541
|
-
width: '
|
|
1542
|
-
height: '
|
|
1671
|
+
width: '28px',
|
|
1672
|
+
height: '28px',
|
|
1543
1673
|
boxShadow: '0 1px 2px rgba(0,0,0,0.05)',
|
|
1544
1674
|
margin: '0 1px',
|
|
1675
|
+
cursor: 'pointer',
|
|
1545
1676
|
}}
|
|
1546
1677
|
>
|
|
1547
1678
|
<img
|
|
@@ -1550,8 +1681,8 @@ const DataGrid = forwardRef(
|
|
|
1550
1681
|
data-tooltip-id="system-tooltip"
|
|
1551
1682
|
data-tooltip-content={tooltipContent}
|
|
1552
1683
|
style={{
|
|
1553
|
-
maxHeight: '
|
|
1554
|
-
maxWidth: '
|
|
1684
|
+
maxHeight: '20px',
|
|
1685
|
+
maxWidth: '20px',
|
|
1555
1686
|
objectFit: 'contain',
|
|
1556
1687
|
}}
|
|
1557
1688
|
/>
|
|
@@ -1586,10 +1717,11 @@ const DataGrid = forwardRef(
|
|
|
1586
1717
|
'var(--light-bg-color, #f5f5f5)',
|
|
1587
1718
|
borderRadius: '4px',
|
|
1588
1719
|
padding: '4px',
|
|
1589
|
-
width: '
|
|
1590
|
-
height: '
|
|
1720
|
+
width: '28px',
|
|
1721
|
+
height: '28px',
|
|
1591
1722
|
boxShadow: '0 1px 2px rgba(0,0,0,0.05)',
|
|
1592
1723
|
margin: '0 1px',
|
|
1724
|
+
cursor: 'pointer',
|
|
1593
1725
|
}}
|
|
1594
1726
|
>
|
|
1595
1727
|
<IconComponent
|
|
@@ -2133,7 +2265,14 @@ const DataGrid = forwardRef(
|
|
|
2133
2265
|
|
|
2134
2266
|
const value = `${addressValue} ${stateValue}`;
|
|
2135
2267
|
|
|
2136
|
-
return
|
|
2268
|
+
return (
|
|
2269
|
+
<CellWithTooltip
|
|
2270
|
+
value={value}
|
|
2271
|
+
columnType="address"
|
|
2272
|
+
>
|
|
2273
|
+
<span>{value}</span>
|
|
2274
|
+
</CellWithTooltip>
|
|
2275
|
+
);
|
|
2137
2276
|
},
|
|
2138
2277
|
};
|
|
2139
2278
|
case 'age':
|
|
@@ -2190,11 +2329,17 @@ const DataGrid = forwardRef(
|
|
|
2190
2329
|
name: `${column.type}.${column.id}`,
|
|
2191
2330
|
sortable: false,
|
|
2192
2331
|
render: ({ data }) => {
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2332
|
+
const value = data[column.id]
|
|
2333
|
+
? 'Yes'
|
|
2334
|
+
: 'No';
|
|
2335
|
+
return (
|
|
2336
|
+
<CellWithTooltip
|
|
2337
|
+
value={data[column.id]}
|
|
2338
|
+
columnType="boolean"
|
|
2339
|
+
>
|
|
2340
|
+
<span>{value}</span>
|
|
2341
|
+
</CellWithTooltip>
|
|
2342
|
+
);
|
|
2198
2343
|
},
|
|
2199
2344
|
};
|
|
2200
2345
|
case 'coding':
|
|
@@ -2278,7 +2423,14 @@ const DataGrid = forwardRef(
|
|
|
2278
2423
|
data[column.id]
|
|
2279
2424
|
).format('$0,0.00');
|
|
2280
2425
|
|
|
2281
|
-
return
|
|
2426
|
+
return (
|
|
2427
|
+
<CellWithTooltip
|
|
2428
|
+
value={data[column.id]}
|
|
2429
|
+
columnType="currency"
|
|
2430
|
+
>
|
|
2431
|
+
<span>{data}</span>
|
|
2432
|
+
</CellWithTooltip>
|
|
2433
|
+
);
|
|
2282
2434
|
} else {
|
|
2283
2435
|
return null;
|
|
2284
2436
|
}
|
|
@@ -2328,9 +2480,14 @@ const DataGrid = forwardRef(
|
|
|
2328
2480
|
}
|
|
2329
2481
|
|
|
2330
2482
|
return (
|
|
2331
|
-
<
|
|
2332
|
-
{
|
|
2333
|
-
|
|
2483
|
+
<CellWithTooltip
|
|
2484
|
+
value={data[column.id]}
|
|
2485
|
+
columnType="date"
|
|
2486
|
+
>
|
|
2487
|
+
<span style={columnStyle}>
|
|
2488
|
+
{value}
|
|
2489
|
+
</span>
|
|
2490
|
+
</CellWithTooltip>
|
|
2334
2491
|
);
|
|
2335
2492
|
} else {
|
|
2336
2493
|
return null;
|
|
@@ -2451,7 +2608,13 @@ const DataGrid = forwardRef(
|
|
|
2451
2608
|
}}
|
|
2452
2609
|
>
|
|
2453
2610
|
<option value="">
|
|
2454
|
-
Select
|
|
2611
|
+
Select{' '}
|
|
2612
|
+
{/^[aeiouAEIOU]/.test(
|
|
2613
|
+
column.label
|
|
2614
|
+
)
|
|
2615
|
+
? 'an'
|
|
2616
|
+
: 'a'}{' '}
|
|
2617
|
+
{column.label}
|
|
2455
2618
|
</option>
|
|
2456
2619
|
{dropdownData[column.id] &&
|
|
2457
2620
|
dropdownData[column.id].length >
|
|
@@ -2505,7 +2668,7 @@ const DataGrid = forwardRef(
|
|
|
2505
2668
|
data-tooltip-id={`system-tooltip`}
|
|
2506
2669
|
data-tooltip-content={`Download File ${file.file_name}`}
|
|
2507
2670
|
strokeWidth={2}
|
|
2508
|
-
size={
|
|
2671
|
+
size={14}
|
|
2509
2672
|
className={
|
|
2510
2673
|
styles.tdaction
|
|
2511
2674
|
}
|
|
@@ -2537,7 +2700,7 @@ const DataGrid = forwardRef(
|
|
|
2537
2700
|
.file_name
|
|
2538
2701
|
}`}
|
|
2539
2702
|
strokeWidth={2}
|
|
2540
|
-
size={
|
|
2703
|
+
size={14}
|
|
2541
2704
|
className={
|
|
2542
2705
|
styles.tdaction
|
|
2543
2706
|
}
|
|
@@ -2622,7 +2785,14 @@ const DataGrid = forwardRef(
|
|
|
2622
2785
|
column.jsonData
|
|
2623
2786
|
];
|
|
2624
2787
|
|
|
2625
|
-
return
|
|
2788
|
+
return (
|
|
2789
|
+
<CellWithTooltip
|
|
2790
|
+
value={data}
|
|
2791
|
+
columnType="json"
|
|
2792
|
+
>
|
|
2793
|
+
<span>{data}</span>
|
|
2794
|
+
</CellWithTooltip>
|
|
2795
|
+
);
|
|
2626
2796
|
} else {
|
|
2627
2797
|
return null;
|
|
2628
2798
|
}
|
|
@@ -2668,7 +2838,14 @@ const DataGrid = forwardRef(
|
|
|
2668
2838
|
];
|
|
2669
2839
|
}
|
|
2670
2840
|
|
|
2671
|
-
return
|
|
2841
|
+
return (
|
|
2842
|
+
<CellWithTooltip
|
|
2843
|
+
value={newData}
|
|
2844
|
+
columnType="option"
|
|
2845
|
+
>
|
|
2846
|
+
<span>{newData}</span>
|
|
2847
|
+
</CellWithTooltip>
|
|
2848
|
+
);
|
|
2672
2849
|
} else {
|
|
2673
2850
|
return null;
|
|
2674
2851
|
}
|
|
@@ -2682,9 +2859,14 @@ const DataGrid = forwardRef(
|
|
|
2682
2859
|
render: ({ data }) => {
|
|
2683
2860
|
if (data) {
|
|
2684
2861
|
return (
|
|
2685
|
-
<
|
|
2686
|
-
{column.placeholder}
|
|
2687
|
-
|
|
2862
|
+
<CellWithTooltip
|
|
2863
|
+
value={column.placeholder}
|
|
2864
|
+
columnType="placeholder"
|
|
2865
|
+
>
|
|
2866
|
+
<span>
|
|
2867
|
+
{column.placeholder}
|
|
2868
|
+
</span>
|
|
2869
|
+
</CellWithTooltip>
|
|
2688
2870
|
);
|
|
2689
2871
|
} else {
|
|
2690
2872
|
return null;
|
|
@@ -2772,7 +2954,14 @@ const DataGrid = forwardRef(
|
|
|
2772
2954
|
typeof value === 'string' ||
|
|
2773
2955
|
typeof value === 'number'
|
|
2774
2956
|
) {
|
|
2775
|
-
return
|
|
2957
|
+
return (
|
|
2958
|
+
<CellWithTooltip
|
|
2959
|
+
value={value}
|
|
2960
|
+
columnType="relation"
|
|
2961
|
+
>
|
|
2962
|
+
<span>{value}</span>
|
|
2963
|
+
</CellWithTooltip>
|
|
2964
|
+
);
|
|
2776
2965
|
} else {
|
|
2777
2966
|
return null;
|
|
2778
2967
|
}
|
|
@@ -2923,7 +3112,14 @@ const DataGrid = forwardRef(
|
|
|
2923
3112
|
result += values.join('<br />');
|
|
2924
3113
|
}
|
|
2925
3114
|
|
|
2926
|
-
return
|
|
3115
|
+
return (
|
|
3116
|
+
<CellWithTooltip
|
|
3117
|
+
value={result}
|
|
3118
|
+
columnType="relationArray"
|
|
3119
|
+
>
|
|
3120
|
+
<span>{parse(result)}</span>
|
|
3121
|
+
</CellWithTooltip>
|
|
3122
|
+
);
|
|
2927
3123
|
// }
|
|
2928
3124
|
} else {
|
|
2929
3125
|
let result = '';
|
|
@@ -2954,7 +3150,14 @@ const DataGrid = forwardRef(
|
|
|
2954
3150
|
: '';
|
|
2955
3151
|
}
|
|
2956
3152
|
|
|
2957
|
-
return
|
|
3153
|
+
return (
|
|
3154
|
+
<CellWithTooltip
|
|
3155
|
+
value={result}
|
|
3156
|
+
columnType="relationArray"
|
|
3157
|
+
>
|
|
3158
|
+
<span>{parse(result)}</span>
|
|
3159
|
+
</CellWithTooltip>
|
|
3160
|
+
);
|
|
2958
3161
|
}
|
|
2959
3162
|
|
|
2960
3163
|
return null;
|
|
@@ -2967,9 +3170,14 @@ const DataGrid = forwardRef(
|
|
|
2967
3170
|
render: ({ data }) => {
|
|
2968
3171
|
if (data && data[column.id]) {
|
|
2969
3172
|
return (
|
|
2970
|
-
<
|
|
2971
|
-
{
|
|
2972
|
-
|
|
3173
|
+
<CellWithTooltip
|
|
3174
|
+
value={data[column.id]}
|
|
3175
|
+
columnType="richtext"
|
|
3176
|
+
>
|
|
3177
|
+
<span>
|
|
3178
|
+
{parse(data[column.id])}
|
|
3179
|
+
</span>
|
|
3180
|
+
</CellWithTooltip>
|
|
2973
3181
|
);
|
|
2974
3182
|
} else {
|
|
2975
3183
|
return null;
|
|
@@ -2990,7 +3198,14 @@ const DataGrid = forwardRef(
|
|
|
2990
3198
|
}
|
|
2991
3199
|
});
|
|
2992
3200
|
|
|
2993
|
-
return
|
|
3201
|
+
return (
|
|
3202
|
+
<CellWithTooltip
|
|
3203
|
+
value={stage}
|
|
3204
|
+
columnType="stage"
|
|
3205
|
+
>
|
|
3206
|
+
<span>{stage}</span>
|
|
3207
|
+
</CellWithTooltip>
|
|
3208
|
+
);
|
|
2994
3209
|
},
|
|
2995
3210
|
};
|
|
2996
3211
|
case 'time':
|
|
@@ -3014,7 +3229,14 @@ const DataGrid = forwardRef(
|
|
|
3014
3229
|
: 'hh:mm A'
|
|
3015
3230
|
);
|
|
3016
3231
|
|
|
3017
|
-
return
|
|
3232
|
+
return (
|
|
3233
|
+
<CellWithTooltip
|
|
3234
|
+
value={data[column.id]}
|
|
3235
|
+
columnType="time"
|
|
3236
|
+
>
|
|
3237
|
+
<span>{data}</span>
|
|
3238
|
+
</CellWithTooltip>
|
|
3239
|
+
);
|
|
3018
3240
|
} else {
|
|
3019
3241
|
return null;
|
|
3020
3242
|
}
|
|
@@ -3041,7 +3263,14 @@ const DataGrid = forwardRef(
|
|
|
3041
3263
|
: 'DD-MM-YYYY hh:mm A'
|
|
3042
3264
|
);
|
|
3043
3265
|
|
|
3044
|
-
return
|
|
3266
|
+
return (
|
|
3267
|
+
<CellWithTooltip
|
|
3268
|
+
value={data[column.id]}
|
|
3269
|
+
columnType="timer"
|
|
3270
|
+
>
|
|
3271
|
+
<span>{data}</span>
|
|
3272
|
+
</CellWithTooltip>
|
|
3273
|
+
);
|
|
3045
3274
|
} else {
|
|
3046
3275
|
let shouldRenderButton = false;
|
|
3047
3276
|
|
|
@@ -3166,7 +3395,7 @@ const DataGrid = forwardRef(
|
|
|
3166
3395
|
data-tooltip-id="system-tooltip"
|
|
3167
3396
|
data-tooltip-content={`${column.label} Timer`}
|
|
3168
3397
|
strokeWidth={2}
|
|
3169
|
-
size={
|
|
3398
|
+
size={14}
|
|
3170
3399
|
style={{
|
|
3171
3400
|
color: 'white',
|
|
3172
3401
|
}}
|
|
@@ -3467,14 +3696,21 @@ const DataGrid = forwardRef(
|
|
|
3467
3696
|
render: ({ data }) => {
|
|
3468
3697
|
if (data && data[column.id]) {
|
|
3469
3698
|
return (
|
|
3470
|
-
<
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
>
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3699
|
+
<CellWithTooltip
|
|
3700
|
+
value={data[column.id]}
|
|
3701
|
+
columnType="url"
|
|
3702
|
+
>
|
|
3703
|
+
<span>
|
|
3704
|
+
<a
|
|
3705
|
+
href={
|
|
3706
|
+
data[column.id]
|
|
3707
|
+
}
|
|
3708
|
+
target="_blank"
|
|
3709
|
+
>
|
|
3710
|
+
Link
|
|
3711
|
+
</a>
|
|
3712
|
+
</span>
|
|
3713
|
+
</CellWithTooltip>
|
|
3478
3714
|
);
|
|
3479
3715
|
} else {
|
|
3480
3716
|
return null;
|
|
@@ -3606,7 +3842,14 @@ const DataGrid = forwardRef(
|
|
|
3606
3842
|
}
|
|
3607
3843
|
|
|
3608
3844
|
return (
|
|
3609
|
-
<
|
|
3845
|
+
<CellWithTooltip
|
|
3846
|
+
value={data[column.id]}
|
|
3847
|
+
columnType="text"
|
|
3848
|
+
>
|
|
3849
|
+
<span>
|
|
3850
|
+
{parse(content)}
|
|
3851
|
+
</span>
|
|
3852
|
+
</CellWithTooltip>
|
|
3610
3853
|
);
|
|
3611
3854
|
} else {
|
|
3612
3855
|
return null;
|
|
@@ -3840,7 +4083,7 @@ const DataGrid = forwardRef(
|
|
|
3840
4083
|
setLimit(ajaxSetting.take);
|
|
3841
4084
|
} else {
|
|
3842
4085
|
const calculateRows = (height) =>
|
|
3843
|
-
Math.floor((height - 100) /
|
|
4086
|
+
Math.floor((height - 100) / 32);
|
|
3844
4087
|
|
|
3845
4088
|
const rowCount = calculateRows(minHeight);
|
|
3846
4089
|
setLimit(rowCount);
|
|
@@ -3952,7 +4195,7 @@ const DataGrid = forwardRef(
|
|
|
3952
4195
|
}
|
|
3953
4196
|
headerProps={headerProps}
|
|
3954
4197
|
idProperty="id"
|
|
3955
|
-
minRowHeight={
|
|
4198
|
+
minRowHeight={32}
|
|
3956
4199
|
onFilterValueChange={(fv) => {
|
|
3957
4200
|
handleFilterChange(fv, filterValue);
|
|
3958
4201
|
}}
|
|
@@ -1112,8 +1112,8 @@ function Field({
|
|
|
1112
1112
|
? settings.options
|
|
1113
1113
|
: options
|
|
1114
1114
|
}
|
|
1115
|
-
isSearchable={settings.isSearchable
|
|
1116
|
-
isCreatable={settings.isCreatable
|
|
1115
|
+
isSearchable={settings.isSearchable ?? true}
|
|
1116
|
+
isCreatable={settings.isCreatable ?? false}
|
|
1117
1117
|
dataOptions={dataOptions}
|
|
1118
1118
|
style={style}
|
|
1119
1119
|
/>
|
|
@@ -25,7 +25,9 @@ const CustomOption = (props) => {
|
|
|
25
25
|
<div className={styles.option}>
|
|
26
26
|
<div className={styles.optionLabel}>{children}</div>
|
|
27
27
|
{data.description && (
|
|
28
|
-
<div className={styles.optionDescription}>
|
|
28
|
+
<div className={styles.optionDescription}>
|
|
29
|
+
{data.description}
|
|
30
|
+
</div>
|
|
29
31
|
)}
|
|
30
32
|
</div>
|
|
31
33
|
</components.Option>
|
|
@@ -40,7 +42,9 @@ const CustomMultiValue = (props) => {
|
|
|
40
42
|
<div className={styles.multiValue}>
|
|
41
43
|
<div className={styles.multiValueLabel}>{children}</div>
|
|
42
44
|
{data.description && (
|
|
43
|
-
<div className={styles.multiValueDescription}>
|
|
45
|
+
<div className={styles.multiValueDescription}>
|
|
46
|
+
{data.description}
|
|
47
|
+
</div>
|
|
44
48
|
)}
|
|
45
49
|
</div>
|
|
46
50
|
</components.MultiValue>
|
|
@@ -111,14 +115,14 @@ function MultiSelect({
|
|
|
111
115
|
if (item.description) return item;
|
|
112
116
|
|
|
113
117
|
// Try to find a matching option with description
|
|
114
|
-
const matchingOption = selectOptions.find(
|
|
115
|
-
opt.id === item.id || opt.value === item.id
|
|
118
|
+
const matchingOption = selectOptions.find(
|
|
119
|
+
(opt) => opt.id === item.id || opt.value === item.id
|
|
116
120
|
);
|
|
117
121
|
|
|
118
122
|
if (matchingOption && matchingOption.description) {
|
|
119
123
|
return {
|
|
120
124
|
...item,
|
|
121
|
-
description: matchingOption.description
|
|
125
|
+
description: matchingOption.description,
|
|
122
126
|
};
|
|
123
127
|
}
|
|
124
128
|
|
|
@@ -130,7 +134,11 @@ function MultiSelect({
|
|
|
130
134
|
const enrichedItem = findMatchingOption(a);
|
|
131
135
|
return {
|
|
132
136
|
value: enrichedItem.id,
|
|
133
|
-
label:
|
|
137
|
+
label:
|
|
138
|
+
enrichedItem.label ||
|
|
139
|
+
enrichedItem.name ||
|
|
140
|
+
enrichedItem.description ||
|
|
141
|
+
'Unknown',
|
|
134
142
|
// Make sure description is preserved
|
|
135
143
|
description: enrichedItem.description || null,
|
|
136
144
|
...enrichedItem,
|
|
@@ -210,7 +218,7 @@ function MultiSelect({
|
|
|
210
218
|
components={{
|
|
211
219
|
SelectList,
|
|
212
220
|
Option: CustomOption,
|
|
213
|
-
MultiValue: CustomMultiValue
|
|
221
|
+
MultiValue: CustomMultiValue,
|
|
214
222
|
}}
|
|
215
223
|
options={selectOptions}
|
|
216
224
|
menuPortalTarget={document.body}
|
|
@@ -301,21 +309,33 @@ function MultiSelect({
|
|
|
301
309
|
padding: '2px 4px',
|
|
302
310
|
minHeight: '30px',
|
|
303
311
|
display: 'flex',
|
|
304
|
-
flexDirection: '
|
|
305
|
-
alignItems: '
|
|
312
|
+
flexDirection: 'row',
|
|
313
|
+
alignItems: 'center',
|
|
314
|
+
backgroundColor: '#e2e8f0',
|
|
315
|
+
borderRadius: '4px',
|
|
306
316
|
}),
|
|
307
317
|
multiValueLabel: (base) => ({
|
|
308
318
|
...base,
|
|
309
|
-
padding: '0',
|
|
319
|
+
padding: '0 4px 0 0',
|
|
310
320
|
display: 'flex',
|
|
311
321
|
flexDirection: 'column',
|
|
312
|
-
|
|
322
|
+
flex: '1',
|
|
323
|
+
minWidth: '0',
|
|
313
324
|
}),
|
|
314
325
|
multiValueRemove: (base) => ({
|
|
315
326
|
...base,
|
|
316
327
|
padding: '0 4px',
|
|
317
|
-
|
|
318
|
-
|
|
328
|
+
display: 'flex',
|
|
329
|
+
alignItems: 'center',
|
|
330
|
+
justifyContent: 'center',
|
|
331
|
+
margin: '0',
|
|
332
|
+
borderRadius: '0 4px 4px 0',
|
|
333
|
+
backgroundColor: 'transparent',
|
|
334
|
+
transition: 'background-color 0.2s ease',
|
|
335
|
+
'&:hover': {
|
|
336
|
+
backgroundColor: '#dc2626',
|
|
337
|
+
color: 'white',
|
|
338
|
+
},
|
|
319
339
|
}),
|
|
320
340
|
}}
|
|
321
341
|
value={selectValue}
|
|
@@ -21,19 +21,20 @@
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
/* Clock button styles */
|
|
24
|
+
/* Clock button styles - Compact version */
|
|
25
25
|
.clockButton {
|
|
26
26
|
display: flex;
|
|
27
27
|
align-items: center;
|
|
28
28
|
justify-content: center;
|
|
29
|
-
width:
|
|
30
|
-
height:
|
|
31
|
-
border-radius:
|
|
29
|
+
width: 28px;
|
|
30
|
+
height: 28px;
|
|
31
|
+
border-radius: 3px;
|
|
32
32
|
background-color: #2b6cb0; /* Matching blue from screenshot */
|
|
33
33
|
border: none;
|
|
34
34
|
cursor: pointer;
|
|
35
35
|
transition: all 0.2s ease;
|
|
36
36
|
margin: 0 auto;
|
|
37
|
+
padding: 0;
|
|
37
38
|
|
|
38
39
|
&:hover {
|
|
39
40
|
background-color: #2563a0; /* Slightly darker blue on hover */
|
|
@@ -109,13 +110,22 @@
|
|
|
109
110
|
justify-content: flex-start;
|
|
110
111
|
align-items: center;
|
|
111
112
|
gap: 0.1rem;
|
|
113
|
+
padding: 3px 0;
|
|
112
114
|
|
|
113
115
|
span {
|
|
114
116
|
display: flex;
|
|
115
117
|
align-items: center;
|
|
116
118
|
position: relative;
|
|
117
|
-
width:
|
|
118
|
-
height:
|
|
119
|
+
width: 20px;
|
|
120
|
+
height: 20px;
|
|
121
|
+
padding: 3px;
|
|
122
|
+
cursor: pointer;
|
|
123
|
+
border-radius: 3px;
|
|
124
|
+
transition: background-color 0.2s ease;
|
|
125
|
+
|
|
126
|
+
&:hover {
|
|
127
|
+
background-color: rgba(var(--primary-rgb, 59, 130, 246), 0.1);
|
|
128
|
+
}
|
|
119
129
|
|
|
120
130
|
svg {
|
|
121
131
|
width: 18px;
|
|
@@ -32,8 +32,12 @@
|
|
|
32
32
|
|
|
33
33
|
.multiValue {
|
|
34
34
|
display: flex;
|
|
35
|
-
flex-direction:
|
|
36
|
-
|
|
35
|
+
flex-direction: row;
|
|
36
|
+
align-items: center;
|
|
37
|
+
gap: 4px;
|
|
38
|
+
background-color: #e2e8f0;
|
|
39
|
+
border-radius: 4px;
|
|
40
|
+
padding: 2px 4px;
|
|
37
41
|
}
|
|
38
42
|
|
|
39
43
|
.multiValueLabel {
|
|
@@ -46,7 +50,12 @@
|
|
|
46
50
|
color: rgba(0, 0, 0, 0.7); /* Darker color for better contrast */
|
|
47
51
|
font-weight: 400;
|
|
48
52
|
line-height: 1.1;
|
|
49
|
-
background-color: rgba(
|
|
53
|
+
background-color: rgba(
|
|
54
|
+
255,
|
|
55
|
+
255,
|
|
56
|
+
255,
|
|
57
|
+
0.7
|
|
58
|
+
); /* Semi-transparent white background */
|
|
50
59
|
padding: 1px 3px;
|
|
51
60
|
border-radius: 2px;
|
|
52
61
|
margin-top: 1px;
|
|
@@ -109,21 +118,34 @@
|
|
|
109
118
|
padding: 2px 4px !important;
|
|
110
119
|
min-height: 30px !important;
|
|
111
120
|
display: flex !important;
|
|
112
|
-
flex-direction:
|
|
113
|
-
align-items:
|
|
121
|
+
flex-direction: row !important;
|
|
122
|
+
align-items: center !important;
|
|
123
|
+
background-color: #e2e8f0 !important;
|
|
124
|
+
border-radius: 4px !important;
|
|
114
125
|
}
|
|
115
126
|
|
|
116
127
|
.visns-select__multi-value__label {
|
|
117
|
-
padding: 0 !important;
|
|
128
|
+
padding: 0 4px 0 0 !important;
|
|
118
129
|
display: flex !important;
|
|
119
130
|
flex-direction: column !important;
|
|
120
|
-
|
|
131
|
+
flex: 1 !important;
|
|
132
|
+
min-width: 0 !important;
|
|
121
133
|
}
|
|
122
134
|
|
|
123
135
|
.visns-select__multi-value__remove {
|
|
124
136
|
padding: 0 4px !important;
|
|
125
|
-
|
|
126
|
-
|
|
137
|
+
display: flex !important;
|
|
138
|
+
align-items: center !important;
|
|
139
|
+
justify-content: center !important;
|
|
140
|
+
margin: 0 !important;
|
|
141
|
+
border-radius: 0 4px 4px 0 !important;
|
|
142
|
+
background-color: transparent !important;
|
|
143
|
+
transition: background-color 0.2s ease !important;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.visns-select__multi-value__remove:hover {
|
|
147
|
+
background-color: #dc2626 !important;
|
|
148
|
+
color: white !important;
|
|
127
149
|
}
|
|
128
150
|
|
|
129
151
|
/* Fix for modals */
|
|
@@ -21,9 +21,15 @@
|
|
|
21
21
|
border-radius: 0 !important;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
/* Ensure consistent border radius for cells */
|
|
24
|
+
/* Ensure consistent border radius for cells and reduce padding for compact rows */
|
|
25
25
|
.InovuaReactDataGrid__cell {
|
|
26
26
|
border-radius: 0 !important;
|
|
27
|
+
padding: 6px 8px !important; /* Reduced from default padding */
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* Reduce header cell padding as well */
|
|
31
|
+
.InovuaReactDataGrid__header-cell {
|
|
32
|
+
padding: 6px 8px !important;
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
/* Fix for focus outline */
|
|
@@ -33,9 +39,9 @@
|
|
|
33
39
|
box-shadow: 0 0 0 2px rgba(var(--primary-rgb, 59, 130, 246), 0.2) !important;
|
|
34
40
|
}
|
|
35
41
|
|
|
36
|
-
/* Checkbox column improvements for touch devices */
|
|
42
|
+
/* Checkbox column improvements for touch devices - Compact version */
|
|
37
43
|
.InovuaReactDataGrid__cell[data-column-id='__checkbox-column'] {
|
|
38
|
-
padding: 8px
|
|
44
|
+
padding: 6px 8px !important; /* Reduced padding for compact rows */
|
|
39
45
|
min-width: 60px !important;
|
|
40
46
|
width: 60px !important;
|
|
41
47
|
max-width: 60px !important;
|
|
@@ -92,9 +98,9 @@
|
|
|
92
98
|
background-color: rgba(var(--primary-rgb, 59, 130, 246), 0.1) !important;
|
|
93
99
|
}
|
|
94
100
|
|
|
95
|
-
/* Header checkbox improvements */
|
|
101
|
+
/* Header checkbox improvements - Compact version */
|
|
96
102
|
.InovuaReactDataGrid__header-cell[data-column-id='__checkbox-column'] {
|
|
97
|
-
padding: 8px
|
|
103
|
+
padding: 6px 8px !important; /* Reduced padding for compact rows */
|
|
98
104
|
min-width: 60px !important;
|
|
99
105
|
width: 60px !important;
|
|
100
106
|
max-width: 60px !important;
|
|
@@ -139,13 +145,13 @@
|
|
|
139
145
|
background-color: inherit !important;
|
|
140
146
|
}
|
|
141
147
|
|
|
142
|
-
/* Mobile-specific improvements */
|
|
148
|
+
/* Mobile-specific improvements - Compact version */
|
|
143
149
|
@media (max-width: 768px) {
|
|
144
150
|
.InovuaReactDataGrid__cell[data-column-id='__checkbox-column'] {
|
|
145
151
|
min-width: 70px !important;
|
|
146
152
|
width: 70px !important;
|
|
147
153
|
max-width: 70px !important;
|
|
148
|
-
padding: 12px
|
|
154
|
+
padding: 8px 12px !important; /* Reduced padding for compact rows */
|
|
149
155
|
}
|
|
150
156
|
|
|
151
157
|
.InovuaReactDataGrid__cell[data-column-id='__checkbox-column']
|
|
@@ -168,7 +174,7 @@
|
|
|
168
174
|
min-width: 70px !important;
|
|
169
175
|
width: 70px !important;
|
|
170
176
|
max-width: 70px !important;
|
|
171
|
-
padding: 12px
|
|
177
|
+
padding: 8px 12px !important; /* Reduced padding for compact rows */
|
|
172
178
|
}
|
|
173
179
|
|
|
174
180
|
.InovuaReactDataGrid__header-cell[data-column-id='__checkbox-column']
|
|
@@ -178,10 +184,10 @@
|
|
|
178
184
|
}
|
|
179
185
|
}
|
|
180
186
|
|
|
181
|
-
/* Touch device specific improvements */
|
|
187
|
+
/* Touch device specific improvements - Compact version */
|
|
182
188
|
@media (hover: none) and (pointer: coarse) {
|
|
183
189
|
.InovuaReactDataGrid__cell[data-column-id='__checkbox-column'] {
|
|
184
|
-
padding:
|
|
190
|
+
padding: 12px !important; /* Reduced padding for compact rows */
|
|
185
191
|
}
|
|
186
192
|
|
|
187
193
|
.InovuaReactDataGrid__cell[data-column-id='__checkbox-column']
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import MultiSelect from '../components/crm/MultiSelect';
|
|
3
|
+
|
|
4
|
+
function MultiSelectStylingTest() {
|
|
5
|
+
const [selectedValues, setSelectedValues] = useState([]);
|
|
6
|
+
|
|
7
|
+
const options = [
|
|
8
|
+
{ id: 1, label: 'Customer Service Representative' },
|
|
9
|
+
{ id: 2, label: 'Dashboard Administrator' },
|
|
10
|
+
{ id: 3, label: 'Project Manager' },
|
|
11
|
+
{ id: 4, label: 'Technical Support Specialist' },
|
|
12
|
+
{ id: 5, label: 'Sales Representative' },
|
|
13
|
+
{ id: 6, label: 'Quality Assurance Analyst' },
|
|
14
|
+
{ id: 7, label: 'Business Development Manager' },
|
|
15
|
+
{ id: 8, label: 'Software Engineer' }
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
const handleChange = (values) => {
|
|
19
|
+
setSelectedValues(values);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div style={{ padding: '20px', maxWidth: '600px', margin: '0 auto' }}>
|
|
24
|
+
<h1>MultiSelect Styling Test</h1>
|
|
25
|
+
<p>This test verifies that the delete/cross icons appear inline with the selected item text, not on a new line.</p>
|
|
26
|
+
|
|
27
|
+
<div style={{ marginBottom: '20px' }}>
|
|
28
|
+
<h3>Test Case: Multi-Select with Long Labels</h3>
|
|
29
|
+
<p>Select multiple items to test the inline styling of delete icons:</p>
|
|
30
|
+
|
|
31
|
+
<MultiSelect
|
|
32
|
+
settings={{
|
|
33
|
+
id: 'stylingTest',
|
|
34
|
+
multi: true
|
|
35
|
+
}}
|
|
36
|
+
multi={true}
|
|
37
|
+
options={options}
|
|
38
|
+
inputValue={selectedValues}
|
|
39
|
+
onChange={handleChange}
|
|
40
|
+
placeholder="Select multiple roles..."
|
|
41
|
+
/>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div style={{ marginTop: '20px', padding: '15px', backgroundColor: '#f5f5f5', borderRadius: '8px' }}>
|
|
45
|
+
<h3>Expected Behavior:</h3>
|
|
46
|
+
<ul>
|
|
47
|
+
<li>✅ Delete icons should appear on the same line as the text</li>
|
|
48
|
+
<li>✅ Selected items should have a consistent height</li>
|
|
49
|
+
<li>✅ Delete icons should be clickable and remove the item</li>
|
|
50
|
+
<li>✅ Hover effect should work on delete icons</li>
|
|
51
|
+
<li>✅ Multiple selections should wrap properly without excessive height</li>
|
|
52
|
+
</ul>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div style={{ marginTop: '20px' }}>
|
|
56
|
+
<h3>Selected Values:</h3>
|
|
57
|
+
<pre style={{ backgroundColor: '#fff', padding: '10px', borderRadius: '4px', border: '1px solid #ddd' }}>
|
|
58
|
+
{JSON.stringify(selectedValues, null, 2)}
|
|
59
|
+
</pre>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<div style={{ marginTop: '20px', fontSize: '14px', color: '#666' }}>
|
|
63
|
+
<p><strong>Testing Instructions:</strong></p>
|
|
64
|
+
<ol>
|
|
65
|
+
<li>Select 2-3 items from the dropdown</li>
|
|
66
|
+
<li>Verify that delete icons (×) appear next to each selected item on the same line</li>
|
|
67
|
+
<li>Hover over delete icons to see the red background effect</li>
|
|
68
|
+
<li>Click delete icons to remove items</li>
|
|
69
|
+
<li>Check that the component height doesn't grow unnecessarily</li>
|
|
70
|
+
</ol>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export default MultiSelectStylingTest;
|