@emporix/component-library 1.8.1 → 1.10.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 +540 -1
- package/dist/components/autocomplete/AutoComplete.d.ts +40 -0
- package/dist/components/autocomplete/AutoComplete.d.ts.map +1 -0
- package/dist/components/autocomplete/index.d.ts +3 -0
- package/dist/components/autocomplete/index.d.ts.map +1 -0
- package/dist/components/buttons/PrimaryButton.d.ts +1 -1
- package/dist/components/buttons/PrimaryButton.d.ts.map +1 -1
- package/dist/components/buttons/SecondaryButton.d.ts +1 -1
- package/dist/components/buttons/SecondaryButton.d.ts.map +1 -1
- package/dist/components/checkbox/Checkbox.d.ts +50 -0
- package/dist/components/checkbox/Checkbox.d.ts.map +1 -0
- package/dist/components/checkbox/index.d.ts +3 -0
- package/dist/components/checkbox/index.d.ts.map +1 -0
- package/dist/components/datatable/DataTable.d.ts +6 -0
- package/dist/components/datatable/DataTable.d.ts.map +1 -0
- package/dist/components/datatable/index.d.ts +5 -0
- package/dist/components/datatable/index.d.ts.map +1 -0
- package/dist/components/datatable/types.d.ts +60 -0
- package/dist/components/datatable/types.d.ts.map +1 -0
- package/dist/components/datatable/useDataTablePagination.d.ts +17 -0
- package/dist/components/datatable/useDataTablePagination.d.ts.map +1 -0
- package/dist/components/dialog/Dialog.d.ts +34 -0
- package/dist/components/dialog/Dialog.d.ts.map +1 -0
- package/dist/components/dialog/index.d.ts +3 -0
- package/dist/components/dialog/index.d.ts.map +1 -0
- package/dist/components/dropdown/Dropdown.d.ts +23 -1
- package/dist/components/dropdown/Dropdown.d.ts.map +1 -1
- package/dist/components/menu/Menu.d.ts +27 -0
- package/dist/components/menu/Menu.d.ts.map +1 -0
- package/dist/components/menu/index.d.ts +3 -0
- package/dist/components/menu/index.d.ts.map +1 -0
- package/dist/components/message/Message.d.ts +31 -0
- package/dist/components/message/Message.d.ts.map +1 -0
- package/dist/components/radiobutton/RadioButton.d.ts +46 -0
- package/dist/components/radiobutton/RadioButton.d.ts.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.es.js +1563 -6009
- package/dist/index.umd.js +1 -553
- package/dist/style.css +1 -1
- package/package.json +10 -4
package/README.md
CHANGED
|
@@ -110,6 +110,9 @@ import { InputText } from '@emporix/component-library'
|
|
|
110
110
|
// Required field
|
|
111
111
|
<InputText label="Name" required placeholder="Enter name" />
|
|
112
112
|
|
|
113
|
+
// With validation error
|
|
114
|
+
<InputText label="Email" required error="Please enter a valid email address" />
|
|
115
|
+
|
|
113
116
|
// Disabled
|
|
114
117
|
<InputText label="Id" placeholder="Enter id" disabled />
|
|
115
118
|
```
|
|
@@ -122,12 +125,335 @@ import { InputText } from '@emporix/component-library'
|
|
|
122
125
|
| tooltip | string | - | If set, shows info icon with this tooltip |
|
|
123
126
|
| required | boolean | false | Shows asterisk on label |
|
|
124
127
|
| inputId | string | - | id for the input (for label association) |
|
|
128
|
+
| error | ReactNode | - | Error text displayed below the input |
|
|
125
129
|
| disabled | boolean | false | Whether the input is disabled |
|
|
126
130
|
| className | string | - | Additional CSS class for the root |
|
|
127
131
|
| data-testid | string | - | Test ID for root and input (suffix `-input`) |
|
|
128
132
|
|
|
129
133
|
All standard HTML input attributes (placeholder, value, onChange, etc.) are supported.
|
|
130
134
|
|
|
135
|
+
### AutoComplete
|
|
136
|
+
|
|
137
|
+
Search input with async suggestions. API inspired by [PrimeReact AutoComplete](https://primereact.org/autocomplete/).
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
import { AutoComplete } from '@emporix/component-library'
|
|
141
|
+
;<AutoComplete
|
|
142
|
+
value={query}
|
|
143
|
+
suggestions={vendors}
|
|
144
|
+
field='name'
|
|
145
|
+
dropdown
|
|
146
|
+
completeMethod={({ query }) => searchVendors(query)}
|
|
147
|
+
onChange={e => setQuery(e.value)}
|
|
148
|
+
onSelect={e => setSelectedVendor(e.value)}
|
|
149
|
+
label='Vendor'
|
|
150
|
+
placeholder='Search vendor...'
|
|
151
|
+
/>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
| Prop | Type | Description |
|
|
155
|
+
| -------------- | -------- | ---------------------------------- |
|
|
156
|
+
| value | string | Controlled input text |
|
|
157
|
+
| suggestions | T[] | Suggestion list from parent |
|
|
158
|
+
| completeMethod | function | Called (debounced) when user types |
|
|
159
|
+
| onChange | function | Input text changes |
|
|
160
|
+
| onSelect | function | Suggestion selected |
|
|
161
|
+
| dropdown | boolean | Show dropdown trigger button |
|
|
162
|
+
| field | string | Suggestion label property |
|
|
163
|
+
|
|
164
|
+
### Checkbox
|
|
165
|
+
|
|
166
|
+
A checkbox built on a native `input type="checkbox"` with PrimeReact-compatible `onChange` events (`{ checked, originalEvent }`). API inspired by [PrimeReact Checkbox](https://primereact.org/checkbox/).
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
import { useState } from 'react'
|
|
170
|
+
import { Checkbox } from '@emporix/component-library'
|
|
171
|
+
|
|
172
|
+
// Controlled single checkbox
|
|
173
|
+
const [checked, setChecked] = useState(false)
|
|
174
|
+
<Checkbox checked={checked} onChange={e => setChecked(e.checked)} />
|
|
175
|
+
|
|
176
|
+
// With label (associate via inputId + htmlFor)
|
|
177
|
+
<Checkbox inputId="remember-me" checked={checked} onChange={e => setChecked(e.checked)} />
|
|
178
|
+
<label htmlFor="remember-me">Remember me</label>
|
|
179
|
+
|
|
180
|
+
// Role toggles (single selection)
|
|
181
|
+
const roles = ['viewer', 'editor', 'manager', 'administrator']
|
|
182
|
+
const [selectedRole, setSelectedRole] = useState<string | null>(null)
|
|
183
|
+
{roles.map(role => (
|
|
184
|
+
<div key={role}>
|
|
185
|
+
<Checkbox
|
|
186
|
+
inputId={role}
|
|
187
|
+
value={role}
|
|
188
|
+
checked={selectedRole === role}
|
|
189
|
+
onChange={e => setSelectedRole(e.checked ? role : null)}
|
|
190
|
+
/>
|
|
191
|
+
<label htmlFor={role}>{role}</label>
|
|
192
|
+
</div>
|
|
193
|
+
))}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
#### Props
|
|
197
|
+
|
|
198
|
+
| Prop | Type | Default | Description |
|
|
199
|
+
| ----------- | -------------------------------- | ------- | ----------------------------------------------- |
|
|
200
|
+
| checked | boolean | false | Whether the checkbox is checked (controlled) |
|
|
201
|
+
| onChange | (e: CheckboxChangeEvent) => void | - | Fired when checked state changes |
|
|
202
|
+
| value | unknown | - | Value associated with the checkbox |
|
|
203
|
+
| inputId | string | - | id for the native input (for label association) |
|
|
204
|
+
| disabled | boolean | false | Whether the checkbox is disabled |
|
|
205
|
+
| invalid | boolean | false | Invalid state for validation styling |
|
|
206
|
+
| name | string | - | Name attribute for form submission |
|
|
207
|
+
| readOnly | boolean | false | Prevents user interaction |
|
|
208
|
+
| required | boolean | false | Marks the field as required |
|
|
209
|
+
| className | string | - | Additional CSS class for the root |
|
|
210
|
+
| data-testid | string | - | Test ID for root and input (suffix `-input`) |
|
|
211
|
+
|
|
212
|
+
Standard HTML input attributes (`aria-label`, `aria-labelledby`, `tabIndex`, etc.) are supported on the native input.
|
|
213
|
+
|
|
214
|
+
### Dialog
|
|
215
|
+
|
|
216
|
+
A modal dialog built on [PrimeReact Dialog](https://primereact.org/dialog/). Use for focused tasks that need a decision or confirmation. Wraps PrimeReact internally with scoped Emporix styles.
|
|
217
|
+
|
|
218
|
+
```tsx
|
|
219
|
+
import { useState } from 'react'
|
|
220
|
+
import {
|
|
221
|
+
Dialog,
|
|
222
|
+
PrimaryButton,
|
|
223
|
+
SecondaryButton,
|
|
224
|
+
} from '@emporix/component-library'
|
|
225
|
+
|
|
226
|
+
function ConfirmDialogExample() {
|
|
227
|
+
const [visible, setVisible] = useState(false)
|
|
228
|
+
|
|
229
|
+
return (
|
|
230
|
+
<>
|
|
231
|
+
<PrimaryButton onClick={() => setVisible(true)}>
|
|
232
|
+
Open dialog
|
|
233
|
+
</PrimaryButton>
|
|
234
|
+
<Dialog
|
|
235
|
+
visible={visible}
|
|
236
|
+
onHide={() => setVisible(false)}
|
|
237
|
+
header='Confirm changes'
|
|
238
|
+
footer={
|
|
239
|
+
<>
|
|
240
|
+
<SecondaryButton onClick={() => setVisible(false)}>
|
|
241
|
+
Cancel
|
|
242
|
+
</SecondaryButton>
|
|
243
|
+
<PrimaryButton onClick={() => setVisible(false)}>
|
|
244
|
+
Save
|
|
245
|
+
</PrimaryButton>
|
|
246
|
+
</>
|
|
247
|
+
}
|
|
248
|
+
style={{ width: '100%', maxWidth: '480px' }}
|
|
249
|
+
>
|
|
250
|
+
<p>Your changes will be applied immediately.</p>
|
|
251
|
+
</Dialog>
|
|
252
|
+
</>
|
|
253
|
+
)
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
#### Props
|
|
258
|
+
|
|
259
|
+
| Prop | Type | Default | Description |
|
|
260
|
+
| --------------- | ------------- | ------- | -------------------------------------------------------- |
|
|
261
|
+
| visible | boolean | - | Whether the dialog is visible |
|
|
262
|
+
| onHide | () => void | - | Called when the dialog closes (overlay, Escape, or icon) |
|
|
263
|
+
| header | ReactNode | - | Dialog title area |
|
|
264
|
+
| footer | ReactNode | - | Footer slot (e.g. action buttons) |
|
|
265
|
+
| children | ReactNode | - | Dialog body content |
|
|
266
|
+
| className | string | - | Additional CSS class on the dialog root |
|
|
267
|
+
| style | CSSProperties | - | Inline styles on the dialog root (e.g. width) |
|
|
268
|
+
| modal | boolean | true | Blocks interaction with the page behind the dialog |
|
|
269
|
+
| dismissableMask | boolean | modal | When true, clicking the mask closes the dialog |
|
|
270
|
+
| closable | boolean | true | Shows the header close icon |
|
|
271
|
+
| draggable | boolean | false | Allows dragging the dialog by its header |
|
|
272
|
+
| data-testid | string | - | Test ID on the dialog root |
|
|
273
|
+
|
|
274
|
+
Requires PrimeReact base CSS and a theme in the host app.
|
|
275
|
+
|
|
276
|
+
### DataTable
|
|
277
|
+
|
|
278
|
+
Encapsulated data table built on [PrimeReact DataTable](https://primereact.org/datatable/). Styles are scoped via CSS modules. Pass `columns`, optional controlled `pagination`, selection, `rowActions`, and an `emptyTemplate` for empty results.
|
|
279
|
+
|
|
280
|
+
Import `@emporix/component-library/styles` once in your app entry, plus PrimeReact base CSS, a theme, and `primeicons` (required for paginator/sort icons).
|
|
281
|
+
|
|
282
|
+
```tsx
|
|
283
|
+
import { useMemo, useState } from 'react'
|
|
284
|
+
import { FilterMatchMode } from 'primereact/api'
|
|
285
|
+
import { DataTable } from '@emporix/component-library'
|
|
286
|
+
import type { DataTablePaginationState } from '@emporix/component-library'
|
|
287
|
+
import '@emporix/component-library/styles'
|
|
288
|
+
import 'primereact/resources/primereact.css'
|
|
289
|
+
import 'primereact/resources/themes/lara-light-cyan/theme.css'
|
|
290
|
+
import 'primeicons/primeicons.css'
|
|
291
|
+
|
|
292
|
+
const columns = [
|
|
293
|
+
{
|
|
294
|
+
columnKey: 'name',
|
|
295
|
+
field: 'name',
|
|
296
|
+
header: 'Name',
|
|
297
|
+
sortable: true,
|
|
298
|
+
filter: true,
|
|
299
|
+
filterMatchMode: FilterMatchMode.CONTAINS,
|
|
300
|
+
showFilterMenu: false,
|
|
301
|
+
showClearButton: false,
|
|
302
|
+
},
|
|
303
|
+
{ columnKey: 'email', field: 'email', header: 'Email', sortable: true },
|
|
304
|
+
]
|
|
305
|
+
|
|
306
|
+
function UsersTable({
|
|
307
|
+
users,
|
|
308
|
+
totalCount,
|
|
309
|
+
loading,
|
|
310
|
+
paginationParams,
|
|
311
|
+
onPage,
|
|
312
|
+
onSort,
|
|
313
|
+
onFilter,
|
|
314
|
+
}) {
|
|
315
|
+
const [selected, setSelected] = useState([])
|
|
316
|
+
|
|
317
|
+
const pagination = useMemo<DataTablePaginationState>(
|
|
318
|
+
() => ({
|
|
319
|
+
...paginationParams,
|
|
320
|
+
totalRecords: totalCount,
|
|
321
|
+
}),
|
|
322
|
+
[paginationParams, totalCount]
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
return (
|
|
326
|
+
<DataTable
|
|
327
|
+
value={users}
|
|
328
|
+
columns={columns}
|
|
329
|
+
dataKey='id'
|
|
330
|
+
loading={loading}
|
|
331
|
+
pagination={pagination}
|
|
332
|
+
onPage={onPage}
|
|
333
|
+
onSort={onSort}
|
|
334
|
+
onFilter={onFilter}
|
|
335
|
+
selectionMode='multiple'
|
|
336
|
+
selection={selected}
|
|
337
|
+
onSelectionChange={setSelected}
|
|
338
|
+
rowActions={user => <button type='button'>Edit</button>}
|
|
339
|
+
currentPageReportTemplate='{first} - {last} of {totalRecords}'
|
|
340
|
+
emptyTemplate={<span>No users found</span>}
|
|
341
|
+
/>
|
|
342
|
+
)
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
#### Props
|
|
347
|
+
|
|
348
|
+
| Prop | Type | Default | Description |
|
|
349
|
+
| ----------------------------- | --------------------------------- | ------- | ---------------------------------------------------------- |
|
|
350
|
+
| value | T[] | - | Row data array (required) |
|
|
351
|
+
| columns | ColumnProps[] | - | Column definitions; `rowActions` appends an actions column |
|
|
352
|
+
| dataKey | string | - | Unique row identifier field |
|
|
353
|
+
| loading | boolean | false | Shows loading overlay |
|
|
354
|
+
| pagination | DataTablePaginationState | - | Controlled pagination/sort/filter state |
|
|
355
|
+
| onPage / onSort / onFilter | function | - | Pagination, sort, and filter event handlers |
|
|
356
|
+
| selectionMode | `'single' \| 'multiple' \| null` | null | Row selection mode |
|
|
357
|
+
| selection / onSelectionChange | T \| T[number] \| null / function | - | Controlled selection state |
|
|
358
|
+
| rowActions | (item) => ReactNode | - | Renders an actions column per row |
|
|
359
|
+
| emptyTemplate | ReactNode \| () => ReactNode | - | Content when `value` is empty |
|
|
360
|
+
| lazy / totalRecords | boolean / number | - | Server-driven data with external total count |
|
|
361
|
+
| className / style | string / CSSProperties | - | Root table styling |
|
|
362
|
+
| data-testid | string | - | Test ID on the table root |
|
|
363
|
+
|
|
364
|
+
Omit `pagination` to use the built-in `useDataTablePagination` hook for local client-side paging.
|
|
365
|
+
|
|
366
|
+
### Dropdown
|
|
367
|
+
|
|
368
|
+
A select-style dropdown for choosing one or more items from a list. Supports optional filter/search, editable input, label, required indicator, and validation errors. Hover and focus styles match the InputText component. API inspired by [PrimeReact Dropdown](https://primereact.org/dropdown/).
|
|
369
|
+
|
|
370
|
+
```tsx
|
|
371
|
+
import { Dropdown } from '@emporix/component-library'
|
|
372
|
+
|
|
373
|
+
const options = [
|
|
374
|
+
{ label: 'GBP - British Pound Sterling', value: 'GBP' },
|
|
375
|
+
{ label: 'EUR - Euro', value: 'EUR' },
|
|
376
|
+
{ label: 'USD - US Dollar', value: 'USD' },
|
|
377
|
+
]
|
|
378
|
+
|
|
379
|
+
// With label, filter, and required
|
|
380
|
+
<Dropdown
|
|
381
|
+
value={currency}
|
|
382
|
+
onChange={e => setCurrency(e.value)}
|
|
383
|
+
options={options}
|
|
384
|
+
optionLabel="label"
|
|
385
|
+
optionValue="value"
|
|
386
|
+
placeholder="Select a currency"
|
|
387
|
+
label="Preferred Currency"
|
|
388
|
+
required
|
|
389
|
+
filter
|
|
390
|
+
/>
|
|
391
|
+
|
|
392
|
+
// With validation error (same styling as InputText)
|
|
393
|
+
<Dropdown
|
|
394
|
+
value={currency}
|
|
395
|
+
onChange={e => setCurrency(e.value)}
|
|
396
|
+
options={options}
|
|
397
|
+
placeholder="Select a currency"
|
|
398
|
+
label="Preferred Currency"
|
|
399
|
+
required
|
|
400
|
+
error="Currency is required"
|
|
401
|
+
/>
|
|
402
|
+
|
|
403
|
+
// Multiple selection
|
|
404
|
+
<Dropdown
|
|
405
|
+
value={currencies}
|
|
406
|
+
onChange={e => setCurrencies(Array.isArray(e.value) ? e.value : [])}
|
|
407
|
+
options={options}
|
|
408
|
+
placeholder="Select currencies"
|
|
409
|
+
label="Currencies"
|
|
410
|
+
multiple
|
|
411
|
+
filter
|
|
412
|
+
/>
|
|
413
|
+
|
|
414
|
+
// Multi-select with removable chips (PrimeReact MultiSelect display="chip" parity)
|
|
415
|
+
<Dropdown
|
|
416
|
+
value={groupIds}
|
|
417
|
+
onChange={e => setGroupIds(Array.isArray(e.value) ? e.value : [])}
|
|
418
|
+
options={groupOptions}
|
|
419
|
+
multiple
|
|
420
|
+
display="chip"
|
|
421
|
+
filter
|
|
422
|
+
placeholder="Select groups"
|
|
423
|
+
label="User groups"
|
|
424
|
+
/>
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
#### Props
|
|
428
|
+
|
|
429
|
+
| Prop | Type | Default | Description |
|
|
430
|
+
| ------------------ | ------------------------- | ---------------------- | --------------------------------------------- |
|
|
431
|
+
| value | unknown | - | Selected value (array when `multiple`) |
|
|
432
|
+
| onChange | (e: ChangeEvent) => void | - | Callback when selection changes |
|
|
433
|
+
| options | T[] | - | Array of options |
|
|
434
|
+
| optionLabel | string | `"label"` | Property name for option label |
|
|
435
|
+
| optionValue | string | `"value"` | Property name for option value |
|
|
436
|
+
| optionDisabled | string | - | Property name for disabled flag on options |
|
|
437
|
+
| placeholder | string | `"Select"` | Placeholder when no value selected |
|
|
438
|
+
| filter | boolean | false | Enable search filter in the overlay |
|
|
439
|
+
| editable | boolean | false | Allow typing in the trigger to filter/select |
|
|
440
|
+
| onEditEnd | (e: EditEndEvent) => void | - | Fired when Enter is pressed in editable mode |
|
|
441
|
+
| multiple | boolean | false | Allow multiple selection |
|
|
442
|
+
| display | `'comma' \| 'chip'` | `'comma'` | Multi-select trigger display mode |
|
|
443
|
+
| maxSelectedLabels | number | - | Max chips before showing `selectedItemsLabel` |
|
|
444
|
+
| selectedItemsLabel | string | `'{0} items selected'` | Overflow label; `{0}` = count |
|
|
445
|
+
| removeIcon | ReactNode | - | Custom icon for chip remove buttons |
|
|
446
|
+
| label | ReactNode | - | Label text above the trigger |
|
|
447
|
+
| error | ReactNode | - | Error text displayed below the trigger |
|
|
448
|
+
| required | boolean | false | Shows asterisk on label |
|
|
449
|
+
| inputId | string | - | id for the trigger (for label association) |
|
|
450
|
+
| disabled | boolean | false | Whether the dropdown is disabled |
|
|
451
|
+
| valueTemplate | (option) => ReactNode | - | Custom render for selected value in trigger |
|
|
452
|
+
| itemTemplate | (option) => ReactNode | - | Custom render for each option in the list |
|
|
453
|
+
| className | string | - | Additional CSS class for the root |
|
|
454
|
+
| panelClassName | string | - | Additional CSS class for the overlay panel |
|
|
455
|
+
| data-testid | string | - | Test ID for root, trigger, panel, and options |
|
|
456
|
+
|
|
131
457
|
### SelectButton
|
|
132
458
|
|
|
133
459
|
Choose single or multiple options using buttons. API inspired by [PrimeReact SelectButton](https://primereact.org/selectbutton/). Selected state uses project primary colors.
|
|
@@ -173,6 +499,70 @@ const options = [
|
|
|
173
499
|
| className | string | - | Additional CSS class |
|
|
174
500
|
| data-testid | string | - | Test ID for root and option buttons |
|
|
175
501
|
|
|
502
|
+
### RadioButton
|
|
503
|
+
|
|
504
|
+
Single-choice selection using native `input[type="radio"]`. API inspired by [PrimeReact RadioButton](https://primereact.org/radiobutton/). Compose multiple `RadioButton` instances with the same `name` to form a group.
|
|
505
|
+
|
|
506
|
+
```tsx
|
|
507
|
+
import { useState } from 'react'
|
|
508
|
+
import { RadioButton } from '@emporix/component-library'
|
|
509
|
+
|
|
510
|
+
function ContentTypePicker() {
|
|
511
|
+
const [contentType, setContentType] = useState('standard')
|
|
512
|
+
|
|
513
|
+
return (
|
|
514
|
+
<div style={{ display: 'flex', gap: '1.5rem' }}>
|
|
515
|
+
<RadioButton
|
|
516
|
+
name='content-type'
|
|
517
|
+
inputId='content-standard'
|
|
518
|
+
value='standard'
|
|
519
|
+
label='Standard'
|
|
520
|
+
checked={contentType === 'standard'}
|
|
521
|
+
onChange={e => setContentType(String(e.value))}
|
|
522
|
+
/>
|
|
523
|
+
<RadioButton
|
|
524
|
+
name='content-type'
|
|
525
|
+
inputId='content-templates'
|
|
526
|
+
value='templates'
|
|
527
|
+
label='Templates'
|
|
528
|
+
checked={contentType === 'templates'}
|
|
529
|
+
onChange={e => setContentType(String(e.value))}
|
|
530
|
+
/>
|
|
531
|
+
<RadioButton
|
|
532
|
+
name='content-type'
|
|
533
|
+
inputId='content-service'
|
|
534
|
+
value='service'
|
|
535
|
+
label='Service'
|
|
536
|
+
checked={contentType === 'service'}
|
|
537
|
+
onChange={e => setContentType(String(e.value))}
|
|
538
|
+
/>
|
|
539
|
+
<RadioButton
|
|
540
|
+
name='content-type'
|
|
541
|
+
inputId='content-vendor'
|
|
542
|
+
value='vendor'
|
|
543
|
+
label='Vendor'
|
|
544
|
+
checked={contentType === 'vendor'}
|
|
545
|
+
onChange={e => setContentType(String(e.value))}
|
|
546
|
+
/>
|
|
547
|
+
</div>
|
|
548
|
+
)
|
|
549
|
+
}
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
#### Props
|
|
553
|
+
|
|
554
|
+
| Prop | Type | Default | Description |
|
|
555
|
+
| ----------- | ------------------------ | ------- | -------------------------------------------------------- |
|
|
556
|
+
| inputId | string | - | id for the native input (auto-generated if omitted) |
|
|
557
|
+
| name | string | - | Shared name for radios in the same group (required) |
|
|
558
|
+
| value | unknown | - | Value of this radio option |
|
|
559
|
+
| checked | boolean | false | Whether this option is selected (controlled) |
|
|
560
|
+
| onChange | (e: ChangeEvent) => void | - | Callback when selected; `e.value` is this option's value |
|
|
561
|
+
| disabled | boolean | false | Whether the radio is disabled |
|
|
562
|
+
| label | ReactNode | - | Optional label beside the control |
|
|
563
|
+
| className | string | - | Additional CSS class for the root |
|
|
564
|
+
| data-testid | string | - | Test ID for root and input |
|
|
565
|
+
|
|
176
566
|
### Tabs
|
|
177
567
|
|
|
178
568
|
A flexible tabs component for organizing content into multiple panels.
|
|
@@ -228,6 +618,35 @@ const tabs: TabItem[] = [
|
|
|
228
618
|
| label | string | Display label for the tab |
|
|
229
619
|
| content | ReactNode | Content to display when active |
|
|
230
620
|
|
|
621
|
+
### Menu
|
|
622
|
+
|
|
623
|
+
Popup and inline action menus. Wraps [PrimeReact Menu](https://primereact.org/menu/) with scoped Emporix styles.
|
|
624
|
+
|
|
625
|
+
```tsx
|
|
626
|
+
import { useRef } from 'react'
|
|
627
|
+
import { Menu, type MenuRef, PrimaryButton } from '@emporix/component-library'
|
|
628
|
+
|
|
629
|
+
function RowActionsMenu() {
|
|
630
|
+
const menuRef = useRef<MenuRef>(null)
|
|
631
|
+
|
|
632
|
+
return (
|
|
633
|
+
<>
|
|
634
|
+
<PrimaryButton onClick={e => menuRef.current?.toggle(e)}>
|
|
635
|
+
Actions
|
|
636
|
+
</PrimaryButton>
|
|
637
|
+
<Menu
|
|
638
|
+
ref={menuRef}
|
|
639
|
+
popup
|
|
640
|
+
model={[
|
|
641
|
+
{ label: 'Edit', command: () => onEdit() },
|
|
642
|
+
{ label: 'Delete', disabled: true },
|
|
643
|
+
]}
|
|
644
|
+
/>
|
|
645
|
+
</>
|
|
646
|
+
)
|
|
647
|
+
}
|
|
648
|
+
```
|
|
649
|
+
|
|
231
650
|
### Tooltip
|
|
232
651
|
|
|
233
652
|
Floating overlays on hover or focus, similar to [PrimeReact Tooltip](https://primereact.org/tooltip/). Use **children mode** to wrap a button, input, or other component, or **selector mode** with a CSS `target` and `data-pr-tooltip` / `data-pr-position` on matching elements.
|
|
@@ -277,6 +696,43 @@ import { Tooltip } from '@emporix/component-library'
|
|
|
277
696
|
|
|
278
697
|
Selector targets can override position per element with `data-pr-position`. Escape closes the tooltip.
|
|
279
698
|
|
|
699
|
+
### Message
|
|
700
|
+
|
|
701
|
+
Inline alert banners for contextual feedback, similar to [PrimeReact Message](https://primereact.org/message/). Use `text` for plain copy or `children` for richer content. Default severity icons come from `react-icons`.
|
|
702
|
+
|
|
703
|
+
```tsx
|
|
704
|
+
import { Message } from '@emporix/component-library'
|
|
705
|
+
|
|
706
|
+
// Predefined entity warning (management-dashboard pattern)
|
|
707
|
+
{isPredefined && (
|
|
708
|
+
<Message
|
|
709
|
+
severity="info"
|
|
710
|
+
text="This group is predefined and cannot be modified."
|
|
711
|
+
/>
|
|
712
|
+
)}
|
|
713
|
+
|
|
714
|
+
// Other severities
|
|
715
|
+
<Message severity="success" text="Your changes were saved." />
|
|
716
|
+
<Message severity="warn" text="Review settings before continuing." />
|
|
717
|
+
<Message severity="error" text="Unable to complete the request." />
|
|
718
|
+
|
|
719
|
+
// Rich content
|
|
720
|
+
<Message severity="info">
|
|
721
|
+
<strong>Note:</strong> editing is disabled for system groups.
|
|
722
|
+
</Message>
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
#### Props
|
|
726
|
+
|
|
727
|
+
| Prop | Type | Default | Description |
|
|
728
|
+
| ----------- | ---------------------------------------- | ------- | ----------------------------------------------- |
|
|
729
|
+
| severity | `info` \| `success` \| `warn` \| `error` | `info` | Visual severity of the banner |
|
|
730
|
+
| text | string | - | Plain text content; ignored when `children` set |
|
|
731
|
+
| children | ReactNode | - | Rich message body |
|
|
732
|
+
| icon | ReactNode | - | Custom icon; default icon shown per severity |
|
|
733
|
+
| className | string | - | Additional CSS class on the root element |
|
|
734
|
+
| data-testid | string | - | Test ID for testing |
|
|
735
|
+
|
|
280
736
|
### Accordion
|
|
281
737
|
|
|
282
738
|
Collapsible content panels grouped in tabs.
|
|
@@ -406,7 +862,7 @@ The component library provides a minimal set of CSS variables that are actually
|
|
|
406
862
|
|
|
407
863
|
#### Focus Ring
|
|
408
864
|
|
|
409
|
-
Reusable focus outline for inputs, dropdowns, SelectButton, etc. Override to change focus appearance globally.
|
|
865
|
+
Reusable focus outline for inputs, dropdowns, SelectButton, RadioButton, etc. Override to change focus appearance globally.
|
|
410
866
|
|
|
411
867
|
```css
|
|
412
868
|
--focus-ring-box-shadow: 0 0 0 2px rgba(38, 101, 183, 0.35);
|
|
@@ -419,6 +875,23 @@ Reusable focus outline for inputs, dropdowns, SelectButton, etc. Override to cha
|
|
|
419
875
|
--color-tooltip-text: #ffffff;
|
|
420
876
|
```
|
|
421
877
|
|
|
878
|
+
#### Message
|
|
879
|
+
|
|
880
|
+
```css
|
|
881
|
+
--color-message-info-bg: #e9e9ff;
|
|
882
|
+
--color-message-info-border: #696cff;
|
|
883
|
+
--color-message-info-icon: #696cff;
|
|
884
|
+
--color-message-success-bg: #e4f8f0;
|
|
885
|
+
--color-message-success-border: #1ea97c;
|
|
886
|
+
--color-message-success-icon: #1ea97c;
|
|
887
|
+
--color-message-warn-bg: #fff2e2;
|
|
888
|
+
--color-message-warn-border: #cc8925;
|
|
889
|
+
--color-message-warn-icon: #cc8925;
|
|
890
|
+
--color-message-error-bg: #ffe7e6;
|
|
891
|
+
--color-message-error-border: #ff5757;
|
|
892
|
+
--color-message-error-icon: #ff5757;
|
|
893
|
+
```
|
|
894
|
+
|
|
422
895
|
#### Spacing
|
|
423
896
|
|
|
424
897
|
```css
|
|
@@ -595,6 +1068,72 @@ npm run format:check
|
|
|
595
1068
|
|
|
596
1069
|
The project also includes pre-commit hooks that automatically format staged files before commits.
|
|
597
1070
|
|
|
1071
|
+
## Migrating components from management-dashboard (Cursor skill)
|
|
1072
|
+
|
|
1073
|
+
This repository includes a **Cursor Agent Skill** that guides extracting shared UI from [management-dashboard](https://github.com/emporix/management-dashboard) into `@emporix/component-library`. It covers audits, implementation patterns, CSS encapsulation, MD thin wrappers, tests, and rollout.
|
|
1074
|
+
|
|
1075
|
+
**Skill location:** [`.cursor/skills/migrate-to-component-library/`](.cursor/skills/migrate-to-component-library/)
|
|
1076
|
+
|
|
1077
|
+
| File | Purpose |
|
|
1078
|
+
| -------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
|
1079
|
+
| [`SKILL.md`](.cursor/skills/migrate-to-component-library/SKILL.md) | Step-by-step workflow (phases, checklists, anti-patterns) |
|
|
1080
|
+
| [`reference.md`](.cursor/skills/migrate-to-component-library/reference.md) | Code templates, token mapping, Jira ticket outline, verification checklist |
|
|
1081
|
+
|
|
1082
|
+
Related Cursor rules: [`.cursor/rules/component-development.mdc`](.cursor/rules/component-development.mdc), [`.cursor/rules/version-bump-for-features.mdc`](.cursor/rules/version-bump-for-features.mdc).
|
|
1083
|
+
|
|
1084
|
+
### When to use it
|
|
1085
|
+
|
|
1086
|
+
Use the skill when you:
|
|
1087
|
+
|
|
1088
|
+
- Move or extract a component from management-dashboard into this library
|
|
1089
|
+
- Migrate PrimeReact-based MD components (e.g. tables, dialogs) with scoped styles
|
|
1090
|
+
- Work on [COP-5597 MD Modularization](https://emporix.atlassian.net/browse/COP-5597) stories
|
|
1091
|
+
|
|
1092
|
+
**Reference migration:** `DataTable` (from MD `MdDataTable`) — library export is `DataTable`; MD keeps the existing `MdDataTable` implementation unchanged. Pilot screens import `DataTable` directly from `@emporix/component-library` (or use a thin `Md*` wrapper if preferred).
|
|
1093
|
+
|
|
1094
|
+
### How to invoke the skill in Cursor
|
|
1095
|
+
|
|
1096
|
+
**Option 1 — Mention the skill in chat (recommended)**
|
|
1097
|
+
|
|
1098
|
+
```
|
|
1099
|
+
Migrate [ComponentName] from management-dashboard to component-library
|
|
1100
|
+
using the migrate-to-component-library skill.
|
|
1101
|
+
```
|
|
1102
|
+
|
|
1103
|
+
**Option 2 — Reference by name**
|
|
1104
|
+
|
|
1105
|
+
Ask Cursor to read and follow `migrate-to-component-library` before starting the task.
|
|
1106
|
+
|
|
1107
|
+
**Option 3 — Explicit file reference**
|
|
1108
|
+
|
|
1109
|
+
```
|
|
1110
|
+
Follow .cursor/skills/migrate-to-component-library/SKILL.md to migrate X.
|
|
1111
|
+
```
|
|
1112
|
+
|
|
1113
|
+
Cursor loads the skill automatically when your request matches its description (component extraction, MD → library migration, COP modularization).
|
|
1114
|
+
|
|
1115
|
+
### What the skill walks you through
|
|
1116
|
+
|
|
1117
|
+
1. **Audit** — find consumers, related SCSS, and PrimeReact dependencies in MD
|
|
1118
|
+
2. **Ask you to choose a pattern** (required before any implementation)
|
|
1119
|
+
- **Pattern A (custom):** boilerplate + SCSS modules + **same API as PrimeReact** — e.g. `Dropdown`, `InputText`, `SelectButton` (no `primereact` dependency)
|
|
1120
|
+
- **Pattern B (PrimeReact wrapper):** encapsulated `primereact/*` + scoped overrides — e.g. `Toast`, `DataTable`
|
|
1121
|
+
3. **Implement in component-library** — component folder, tests, Storybook, exports, version bump
|
|
1122
|
+
4. **Wire consumer app** — import library component directly in pilot views **or** add a thin `Md*` wrapper; leave existing `Md*` unchanged for unmigrated screens; import `@emporix/component-library/styles`
|
|
1123
|
+
5. **Verify** — `npm run lint`, `npm run test -- --run`, `npm run build:lib` (library); build and manual QA in MD
|
|
1124
|
+
|
|
1125
|
+
See [`reference.md`](.cursor/skills/migrate-to-component-library/reference.md) for copy-paste templates (`MdDataTable.tsx` wrapper, `PrimeDataTableOverrides.module.scss`, Jira acceptance criteria).
|
|
1126
|
+
|
|
1127
|
+
### Naming convention (library vs MD)
|
|
1128
|
+
|
|
1129
|
+
| Layer | Name | Example |
|
|
1130
|
+
| ----------------------- | --------------------------------------- | ---------------------------------------------------------------------- |
|
|
1131
|
+
| Component library | Generic, reusable name | `DataTable`, `PrimaryButton` |
|
|
1132
|
+
| Consumer app (existing) | Keep original `Md*` component unchanged | `MdDataTable` (legacy implementation) |
|
|
1133
|
+
| Consumer app (pilot) | Direct library import or thin wrapper | `import { DataTable } from '@emporix/component-library'` in pilot view |
|
|
1134
|
+
|
|
1135
|
+
This keeps existing import paths stable (`import MdDataTable from 'components/MdDataTable'`) while pilot screens opt in via direct library import or an optional wrapper.
|
|
1136
|
+
|
|
598
1137
|
## Contributing
|
|
599
1138
|
|
|
600
1139
|
1. Fork the repository
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
export interface AutoCompleteCompleteEvent {
|
|
3
|
+
query: string;
|
|
4
|
+
}
|
|
5
|
+
export interface AutoCompleteChangeEvent {
|
|
6
|
+
value: string;
|
|
7
|
+
originalEvent: React.SyntheticEvent;
|
|
8
|
+
}
|
|
9
|
+
export interface AutoCompleteSelectEvent<T> {
|
|
10
|
+
value: T;
|
|
11
|
+
originalEvent: React.SyntheticEvent;
|
|
12
|
+
}
|
|
13
|
+
export interface AutoCompleteProps<T extends Record<string, unknown>> {
|
|
14
|
+
/** Display text in the input (controlled). */
|
|
15
|
+
value: string;
|
|
16
|
+
/** Suggestion list from the parent (updated via completeMethod). */
|
|
17
|
+
suggestions: T[];
|
|
18
|
+
/** Called when the user types; parent should fetch/filter suggestions. */
|
|
19
|
+
completeMethod: (e: AutoCompleteCompleteEvent) => void;
|
|
20
|
+
/** Property on suggestion objects used for display (default: first string field). */
|
|
21
|
+
field?: keyof T | string;
|
|
22
|
+
/** Fired when the input text changes. */
|
|
23
|
+
onChange: (e: AutoCompleteChangeEvent) => void;
|
|
24
|
+
/** Fired when a suggestion is selected. */
|
|
25
|
+
onSelect?: (e: AutoCompleteSelectEvent<T>) => void;
|
|
26
|
+
/** Show dropdown button to open suggestions. */
|
|
27
|
+
dropdown?: boolean;
|
|
28
|
+
disabled?: boolean;
|
|
29
|
+
placeholder?: string;
|
|
30
|
+
label?: React.ReactNode;
|
|
31
|
+
error?: React.ReactNode;
|
|
32
|
+
className?: string;
|
|
33
|
+
inputId?: string;
|
|
34
|
+
'data-testid'?: string;
|
|
35
|
+
}
|
|
36
|
+
export declare const AutoComplete: {
|
|
37
|
+
<T extends Record<string, unknown>>({ value, suggestions, completeMethod, field, onChange, onSelect, dropdown, disabled, placeholder, label, error, className, inputId: inputIdProp, "data-testid": testId, }: AutoCompleteProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
38
|
+
displayName: string;
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=AutoComplete.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AutoComplete.d.ts","sourceRoot":"","sources":["../../../src/components/autocomplete/AutoComplete.tsx"],"names":[],"mappings":"AAAA,OAAO,KAON,MAAM,OAAO,CAAA;AAId,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,KAAK,CAAC,cAAc,CAAA;CACpC;AAED,MAAM,WAAW,uBAAuB,CAAC,CAAC;IACxC,KAAK,EAAE,CAAC,CAAA;IACR,aAAa,EAAE,KAAK,CAAC,cAAc,CAAA;CACpC;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAClE,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAA;IACb,oEAAoE;IACpE,WAAW,EAAE,CAAC,EAAE,CAAA;IAChB,0EAA0E;IAC1E,cAAc,EAAE,CAAC,CAAC,EAAE,yBAAyB,KAAK,IAAI,CAAA;IACtD,qFAAqF;IACrF,KAAK,CAAC,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IACxB,yCAAyC;IACzC,QAAQ,EAAE,CAAC,CAAC,EAAE,uBAAuB,KAAK,IAAI,CAAA;IAC9C,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,uBAAuB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IAClD,gDAAgD;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACvB,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACvB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAgBD,eAAO,MAAM,YAAY;KAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,6KAe3D,iBAAiB,CAAC,CAAC,CAAC;;CA8NtB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/autocomplete/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,YAAY,EACV,iBAAiB,EACjB,uBAAuB,EACvB,yBAAyB,EACzB,uBAAuB,GACxB,MAAM,gBAAgB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PrimaryButton.d.ts","sourceRoot":"","sources":["../../../src/components/buttons/PrimaryButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;OAEG;IACH,OAAO,CAAC,EAAE,
|
|
1
|
+
{"version":3,"file":"PrimaryButton.d.ts","sourceRoot":"","sources":["../../../src/components/buttons/PrimaryButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;OAEG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAA;IACpD;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,eAAO,MAAM,aAAa,GAAI,sFAQ3B,kBAAkB,4CAsBpB,CAAA"}
|