@emporix/cockpit-component-library 0.0.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.
package/README.md ADDED
@@ -0,0 +1,567 @@
1
+ # Emporix Cockpit Component Library
2
+
3
+ A modern React component library built with TypeScript, Vite, and SCSS modules.
4
+
5
+ ## Features
6
+
7
+ - ๐Ÿš€ **Modern Stack**: Built with Vite, TypeScript, and React 18
8
+ - ๐ŸŽจ **Styled Components**: SCSS modules for component styling
9
+ - ๐Ÿ“š **Storybook**: Interactive component documentation
10
+ - ๐Ÿงช **Testing**: Comprehensive test suite with Vitest and React Testing Library
11
+ - ๐Ÿ“ฆ **Library Build**: Optimized for npm package distribution
12
+ - ๐Ÿ”„ **CI/CD**: Automated testing and publishing pipeline
13
+ - ๐ŸŽจ **CSS Variables**: Global theming system with CSS custom properties
14
+
15
+ ## Quick Start
16
+
17
+ ### Installation
18
+
19
+ ```bash
20
+ npm install @emporix/cockpit-component-library
21
+ ```
22
+
23
+ ### Usage
24
+
25
+ **Important:** Import the library styles in your app so component layout and appearance stay consistent. If you skip this step, components may be unstyled or layout may break.
26
+
27
+ #### Method 1: Import styles in your main CSS file (Recommended)
28
+
29
+ ```css
30
+ /* In your main CSS file (e.g., index.css, App.css) */
31
+ @import '@emporix/cockpit-component-library/styles';
32
+ ```
33
+
34
+ ```tsx
35
+ import { PrimaryButton } from '@emporix/cockpit-component-library'
36
+
37
+ const MyComponent = () => {
38
+ return (
39
+ <PrimaryButton onClick={() => console.log('Clicked!')}>
40
+ Click me
41
+ </PrimaryButton>
42
+ )
43
+ }
44
+ ```
45
+
46
+ #### Method 2: Import styles in your main JavaScript/TypeScript file
47
+
48
+ ```tsx
49
+ import { PrimaryButton } from '@emporix/cockpit-component-library'
50
+ import '@emporix/cockpit-component-library/styles'
51
+
52
+ const MyComponent = () => {
53
+ return (
54
+ <PrimaryButton onClick={() => console.log('Clicked!')}>
55
+ Click me
56
+ </PrimaryButton>
57
+ )
58
+ }
59
+ ```
60
+
61
+ #### Method 3: Copy CSS file manually (Fallback)
62
+
63
+ If the above methods don't work, copy the CSS file from `node_modules/@emporix/cockpit-component-library/dist/style.css` to your project and import it directly.
64
+
65
+ ## Components
66
+
67
+ ### PrimaryButton
68
+
69
+ A simple, focused button component for primary actions.
70
+
71
+ ```tsx
72
+ import { PrimaryButton } from '@emporix/cockpit-component-library'
73
+
74
+ // Basic usage
75
+ <PrimaryButton label="Click me" />
76
+
77
+ // With disabled state
78
+ <PrimaryButton label="Disabled Button" disabled />
79
+
80
+ // With click handler
81
+ <PrimaryButton label="Confirm Action" onClick={() => alert('Confirmed!')} />
82
+
83
+ // Loading state
84
+ <PrimaryButton label="Saving..." loading />
85
+
86
+ // With icon
87
+ <PrimaryButton label="Download" icon={<FiDownload />} iconPos="left" />
88
+ ```
89
+
90
+ #### Props
91
+
92
+ | Prop | Type | Default | Description |
93
+ | ----------- | ------------------------- | ------- | ------------------------------------------------ |
94
+ | label | string | - | Text label displayed inside the button |
95
+ | disabled | boolean | false | Whether button is disabled |
96
+ | loading | boolean | false | Shows loading spinner and disables the button |
97
+ | icon | ReactNode | - | Optional icon shown next to the label or alone |
98
+ | iconPos | `'left' \| 'right'` | 'left' | Icon position relative to the label |
99
+ | onClick | () => void | - | Click handler |
100
+ | className | string | - | Additional CSS class |
101
+ | data-testid | string | - | Test ID for testing |
102
+
103
+ ### InputText
104
+
105
+ A text input with optional label and tooltip. Hover and focus styles match the Dropdown component.
106
+
107
+ ```tsx
108
+ import { InputText } from '@emporix/cockpit-component-library'
109
+
110
+ // With label and info tooltip
111
+ <InputText
112
+ label="Id"
113
+ tooltip="Unique identifier for this record"
114
+ placeholder="Enter id"
115
+ />
116
+
117
+ // Required field
118
+ <InputText label="Name" required placeholder="Enter name" />
119
+
120
+ // Disabled
121
+ <InputText label="Id" placeholder="Enter id" disabled />
122
+
123
+ // Multiline (textarea)
124
+ <InputText
125
+ label="Description"
126
+ textarea
127
+ rows={4}
128
+ placeholder="Enter a longer description"
129
+ />
130
+ ```
131
+
132
+ #### Props
133
+
134
+ | Prop | Type | Default | Description |
135
+ | ----------- | --------- | ------- | ----------------------------------------------------------------------- |
136
+ | label | ReactNode | - | Label text above the input |
137
+ | tooltip | string | - | If set, shows info icon with this tooltip |
138
+ | required | boolean | false | Shows asterisk on label |
139
+ | inputId | string | - | id for the input (for label association) |
140
+ | textarea | boolean | false | When true, renders a `<textarea>` instead of `<input>` |
141
+ | rows | number | 3 | Number of visible rows (textarea only) |
142
+ | disabled | boolean | false | Whether the input is disabled |
143
+ | className | string | - | Additional CSS class for the root |
144
+ | data-testid | string | - | Test ID for root and input (input gets suffix `-input`) |
145
+
146
+ All standard HTML input/textarea attributes (placeholder, value, onChange, etc.) are supported.
147
+
148
+ ### SelectButton
149
+
150
+ Choose single or multiple options using buttons. API inspired by [PrimeReact SelectButton](https://primereact.org/selectbutton/). Selected state uses project primary colors.
151
+
152
+ ```tsx
153
+ import { SelectButton } from '@emporix/cockpit-component-library'
154
+
155
+ const options = [
156
+ { label: 'Off', value: 'off' },
157
+ { label: 'On', value: 'on' },
158
+ ]
159
+
160
+ // Single selection
161
+ <SelectButton
162
+ value={value}
163
+ onChange={(e) => setValue(e.value)}
164
+ options={options}
165
+ />
166
+
167
+ // Multiple selection
168
+ <SelectButton
169
+ value={selected}
170
+ onChange={(e) => setSelected(e.value)}
171
+ options={options}
172
+ multiple
173
+ />
174
+ ```
175
+
176
+ #### Props
177
+
178
+ | Prop | Type | Default | Description |
179
+ | -------------- | ------------------------ | ------- | --------------------------------------- |
180
+ | value | unknown | - | Selected value (or array when multiple) |
181
+ | onChange | (e: ChangeEvent) => void | - | Callback when selection changes |
182
+ | options | T[] | - | Array of options |
183
+ | optionLabel | string | "label" | Property name for option label |
184
+ | optionValue | string | "value" | Property name for option value |
185
+ | optionDisabled | string | - | Property name for disabled flag |
186
+ | multiple | boolean | false | Allow multiple selection |
187
+ | itemTemplate | (option) => ReactNode | - | Custom render for each option |
188
+ | disabled | boolean | false | Disable the whole component |
189
+ | invalid | boolean | false | Validation invalid state |
190
+ | className | string | - | Additional CSS class |
191
+ | data-testid | string | - | Test ID for root and option buttons |
192
+
193
+ ### Tabs
194
+
195
+ A flexible tabs component for organizing content into multiple panels.
196
+
197
+ ```tsx
198
+ import { Tabs, TabItem } from '@emporix/cockpit-component-library'
199
+
200
+ const tabs: TabItem[] = [
201
+ {
202
+ id: 'tab1',
203
+ label: 'First Tab',
204
+ content: <div>First tab content</div>,
205
+ },
206
+ {
207
+ id: 'tab2',
208
+ label: 'Second Tab',
209
+ content: <div>Second tab content</div>,
210
+ },
211
+ ]
212
+
213
+ // Basic usage
214
+ <Tabs
215
+ tabs={tabs}
216
+ activeTabId="tab1"
217
+ onTabChange={(tabId) => setActiveTab(tabId)}
218
+ />
219
+
220
+ // With custom styling and test ID
221
+ <Tabs
222
+ tabs={tabs}
223
+ activeTabId="tab1"
224
+ onTabChange={(tabId) => setActiveTab(tabId)}
225
+ className="custom-tabs"
226
+ data-testid="my-tabs"
227
+ />
228
+ ```
229
+
230
+ #### Props
231
+
232
+ | Prop | Type | Default | Description |
233
+ | ----------- | ----------------------- | ------- | ------------------------------ |
234
+ | tabs | TabItem[] | - | Array of tab items |
235
+ | activeTabId | string | - | ID of the currently active tab |
236
+ | onTabChange | (tabId: string) => void | - | Callback when tab is changed |
237
+ | className | string | - | Additional CSS class |
238
+ | data-testid | string | - | Test ID for testing |
239
+
240
+ #### TabItem Interface
241
+
242
+ | Prop | Type | Description |
243
+ | ------- | --------- | ------------------------------ |
244
+ | id | string | Unique identifier for the tab |
245
+ | label | string | Display label for the tab |
246
+ | content | ReactNode | Content to display when active |
247
+
248
+ ## Theming with CSS Variables
249
+
250
+ The component library uses CSS variables for easy theming. You can customize the appearance by overriding these variables:
251
+
252
+ ```css
253
+ :root {
254
+ --color-primary: #your-primary-color;
255
+ --color-primary-hover: #your-primary-hover-color;
256
+ --border-radius-md: 0.5rem;
257
+ --font-size-lg: 1.2rem;
258
+ /* ... other variables */
259
+ }
260
+ ```
261
+
262
+ ### Available CSS Variables
263
+
264
+ The component library provides a minimal set of CSS variables that are actually used in the codebase. These variables are primarily used for the showcase page styling and can be customized for theming.
265
+
266
+ #### Colors
267
+
268
+ ```css
269
+ --color-primary: #0E99C6;
270
+ --color-primary-hover: #0E87B3;
271
+ --color-dark: #27313B;
272
+ ```
273
+
274
+ #### Text Colors
275
+
276
+ ```css
277
+ --color-text-primary: #FFFFFF;
278
+ --color-text-secondary: #999999;
279
+ ```
280
+
281
+ #### Background Colors
282
+
283
+ ```css
284
+ --color-bg-primary: #ffffff;
285
+ --color-bg-secondary: #f8f9fa;
286
+ ```
287
+
288
+ #### Border Colors
289
+
290
+ ```css
291
+ --color-border-light: #CAD0D6;
292
+ ```
293
+
294
+ #### Focus Ring
295
+
296
+ Reusable focus outline for inputs, dropdowns, SelectButton, etc. Override to change focus appearance globally.
297
+
298
+ ```css
299
+ --focus-ring-box-shadow: 0 0 0 2px rgba(38, 101, 183, 0.35);
300
+ ```
301
+
302
+ #### Spacing
303
+
304
+ ```css
305
+ --spacing-sm: 0.5rem; /* 8px */
306
+ --spacing-md: 1rem; /* 16px */
307
+ --spacing-lg: 1.5rem; /* 24px */
308
+ --spacing-xl: 3rem; /* 48px */
309
+ ```
310
+
311
+ #### Border Radius
312
+
313
+ ```css
314
+ --border-radius-sm: 0.25rem; /* 4px */
315
+ --border-radius-md: 0.375rem; /* 6px */
316
+ --border-radius-lg: 0.5rem; /* 8px */
317
+ ```
318
+
319
+ #### Typography
320
+
321
+ **Font Sizes:**
322
+
323
+ ```css
324
+ --font-size-sm: 0.875rem; /* 14px */
325
+ --font-size-base: 0.875rem; /* 14px */
326
+ --font-size-lg: 1.125rem; /* 18px */
327
+ ```
328
+
329
+ **Font Weights:**
330
+
331
+ ```css
332
+ --font-weight-normal: 400;
333
+ --font-weight-medium: 500;
334
+ --font-weight-semibold: 600;
335
+ --font-weight-bold: 700;
336
+ ```
337
+
338
+ **Line Height:**
339
+
340
+ ```css
341
+ --line-height-base: 1rem;
342
+ ```
343
+
344
+ ### Theming Examples
345
+
346
+ #### Basic Theming
347
+
348
+ ```css
349
+ :root {
350
+ --color-primary: #your-brand-color;
351
+ --color-primary-hover: #your-brand-hover-color;
352
+ --border-radius-md: 0.5rem;
353
+ --font-size-lg: 1.2rem;
354
+ }
355
+ ```
356
+
357
+ #### Dark Theme
358
+
359
+ ```css
360
+ :root {
361
+ --color-bg-primary: #1a1a1a;
362
+ --color-bg-secondary: #2d2d2d;
363
+ --color-text-primary: #ffffff;
364
+ --color-text-secondary: #cccccc;
365
+ --color-border-light: #404040;
366
+ }
367
+ ```
368
+
369
+ #### Custom Component Styling
370
+
371
+ ```css
372
+ .my-custom-component {
373
+ padding: var(--spacing-md);
374
+ border-radius: var(--border-radius-lg);
375
+ background-color: var(--color-bg-primary);
376
+ color: var(--color-text-primary);
377
+ border: 1px solid var(--color-border-light);
378
+ }
379
+ ```
380
+
381
+ ## Development
382
+
383
+ ### Prerequisites
384
+
385
+ - Node.js 18+
386
+ - npm or yarn
387
+
388
+ ### Setup
389
+
390
+ ```bash
391
+ # Clone the repository
392
+ git clone <repository-url>
393
+ cd cockpit-component-library
394
+
395
+ # Install dependencies
396
+ npm install
397
+
398
+ # Start development server
399
+ npm run dev
400
+
401
+ # Start Storybook
402
+ npm run storybook
403
+ ```
404
+
405
+ ### Available Scripts
406
+
407
+ - `npm run dev` - Start development server with showcase
408
+ - `npm run build` - Build the development version
409
+ - `npm run build:lib` - Build the library for distribution
410
+ - `npm run test` - Run tests
411
+ - `npm run test:ui` - Run tests with UI
412
+ - `npm run test:coverage` - Run tests with coverage
413
+ - `npm run storybook` - Start Storybook
414
+ - `npm run build-storybook` - Build Storybook
415
+ - `npm run lint` - Run ESLint
416
+ - `npm run lint:fix` - Fix ESLint issues
417
+ - `npm run type-check` - Run TypeScript type checking
418
+ - `npm run format` - Format code with Prettier
419
+ - `npm run format:check` - Check code formatting
420
+
421
+ ## Testing
422
+
423
+ The library includes comprehensive tests using Vitest and React Testing Library.
424
+
425
+ ```bash
426
+ # Run all tests
427
+ npm test
428
+
429
+ # Run tests with coverage
430
+ npm run test:coverage
431
+
432
+ # Run tests with UI
433
+ npm run test:ui
434
+ ```
435
+
436
+ ## Storybook
437
+
438
+ Interactive component documentation is available in Storybook.
439
+
440
+ ```bash
441
+ # Start Storybook
442
+ npm run storybook
443
+
444
+ # Build Storybook
445
+ npm run build-storybook
446
+ ```
447
+
448
+ ## Building for Distribution
449
+
450
+ The library is built using Vite with optimized settings for npm distribution.
451
+
452
+ ```bash
453
+ # Build the library
454
+ npm run build:lib
455
+ ```
456
+
457
+ The build output includes:
458
+
459
+ - ES modules (`index.es.js`)
460
+ - UMD bundle (`index.umd.js`)
461
+ - TypeScript declarations (`index.d.ts`)
462
+ - CSS styles (`styles.css`)
463
+
464
+ ## Code Formatting
465
+
466
+ This project uses Prettier for consistent code formatting. The configuration is defined in `.prettierrc`.
467
+
468
+ ```bash
469
+ # Format all files
470
+ npm run format
471
+
472
+ # Check if files are formatted correctly
473
+ npm run format:check
474
+ ```
475
+
476
+ The project also includes pre-commit hooks that automatically format staged files before commits.
477
+
478
+ ## Contributing
479
+
480
+ 1. Fork the repository
481
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
482
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
483
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
484
+ 5. Open a Pull Request
485
+
486
+ ### Development Guidelines
487
+
488
+ - Follow TypeScript best practices
489
+ - Write tests for new components
490
+ - Add Storybook stories for components
491
+ - Follow the existing code style
492
+ - Update documentation as needed
493
+ - Use arrow functions for React components (enforced by ESLint)
494
+ - Avoid using `React.FC` type annotation (redundant with modern TypeScript)
495
+ - Use CSS variables for all styling values
496
+ - Follow the established naming conventions for CSS classes
497
+
498
+ ## Troubleshooting
499
+
500
+ ### Import Issues
501
+
502
+ If you encounter import errors like "Failed to resolve entry for package", ensure:
503
+
504
+ 1. The package is properly installed: `npm install @emporix/cockpit-component-library`
505
+ 2. You're using the correct import syntax:
506
+ ```tsx
507
+ import { PrimaryButton } from '@emporix/cockpit-component-library'
508
+ import '@emporix/cockpit-component-library/styles'
509
+ ```
510
+ 3. Your bundler supports ES modules and the package.json exports field
511
+
512
+ ### Styling Issues
513
+
514
+ If the PrimaryButton component appears unstyled or you get "styles not found" errors:
515
+
516
+ 1. **Try Method 1 (Recommended):** Import styles in your main CSS file:
517
+
518
+ ```css
519
+ @import '@emporix/cockpit-component-library/styles';
520
+ ```
521
+
522
+ 2. **Try Method 2:** Import styles in your main JavaScript/TypeScript file:
523
+
524
+ ```tsx
525
+ import '@emporix/cockpit-component-library/styles'
526
+ ```
527
+
528
+ 3. **If both methods fail:** Copy the CSS file manually:
529
+ - Copy `node_modules/@emporix/cockpit-component-library/dist/style.css` to your project
530
+ - Import it in your main CSS file: `@import './path/to/style.css';`
531
+
532
+ 4. **Check your bundler configuration:** Ensure your bundler (Vite, Webpack, etc.) is configured to handle CSS imports from node_modules
533
+
534
+ 5. **Verify the CSS classes:** The component uses SCSS modules with hashed class names (e.g., `_primaryButton_1d0ha_1`, `_primaryButtonDisabled_1d0ha_53`) and CSS variables for theming
535
+
536
+ ### TypeScript Issues
537
+
538
+ If TypeScript can't find types, make sure:
539
+
540
+ 1. The `@types/react` package is installed
541
+ 2. Your `tsconfig.json` includes the library in `node_modules`
542
+
543
+ ### Bundler-Specific Notes
544
+
545
+ #### Vite
546
+
547
+ All methods should work with Vite. Method 1 (CSS import) is recommended.
548
+
549
+ #### Webpack
550
+
551
+ Method 1 and Method 2 should work. If you encounter issues, try Method 3.
552
+
553
+ #### Create React App
554
+
555
+ Method 1 and Method 2 should work. If you encounter issues, try Method 3.
556
+
557
+ #### Next.js
558
+
559
+ Method 1 (CSS import) is recommended. Import the CSS in your `_app.tsx` or `_app.js` file.
560
+
561
+ ## License
562
+
563
+ MIT License - see [LICENSE](LICENSE) file for details.
564
+
565
+ ## Support
566
+
567
+ For support and questions, please open an issue in the repository.
@@ -0,0 +1,29 @@
1
+ import { default as React } from 'react';
2
+ export interface PrimaryButtonProps {
3
+ /**
4
+ * The content of the button
5
+ */
6
+ children: React.ReactNode;
7
+ /**
8
+ * Whether the button is disabled
9
+ */
10
+ disabled?: boolean;
11
+ /**
12
+ * Whether the button is loading
13
+ */
14
+ loading?: boolean;
15
+ /**
16
+ * Click handler
17
+ */
18
+ onClick?: () => void;
19
+ /**
20
+ * Additional CSS class name
21
+ */
22
+ className?: string;
23
+ /**
24
+ * Test ID for testing
25
+ */
26
+ 'data-testid'?: string;
27
+ }
28
+ export declare const PrimaryButton: ({ children, disabled, loading, onClick, className, "data-testid": testId, ...props }: PrimaryButtonProps) => import("react/jsx-runtime").JSX.Element;
29
+ //# sourceMappingURL=PrimaryButton.d.ts.map
@@ -0,0 +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,MAAM,IAAI,CAAA;IACpB;;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"}
@@ -0,0 +1,25 @@
1
+ import { default as React } from 'react';
2
+ export interface SecondaryButtonProps {
3
+ /**
4
+ * The content of the button
5
+ */
6
+ children: React.ReactNode;
7
+ /**
8
+ * Whether the button is disabled
9
+ */
10
+ disabled?: boolean;
11
+ /**
12
+ * Click handler
13
+ */
14
+ onClick?: () => void;
15
+ /**
16
+ * Additional CSS class name
17
+ */
18
+ className?: string;
19
+ /**
20
+ * Test ID for testing
21
+ */
22
+ 'data-testid'?: string;
23
+ }
24
+ export declare const SecondaryButton: ({ children, disabled, onClick, className, "data-testid": testId, ...props }: SecondaryButtonProps) => import("react/jsx-runtime").JSX.Element;
25
+ //# sourceMappingURL=SecondaryButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SecondaryButton.d.ts","sourceRoot":"","sources":["../../../src/components/buttons/SecondaryButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,eAAO,MAAM,eAAe,GAAI,6EAO7B,oBAAoB,4CAqBtB,CAAA"}