summit-registration-lite 7.0.5 → 7.0.7

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.
Files changed (26) hide show
  1. package/dist/components/index.js +85 -32
  2. package/dist/components/index.js.map +1 -1
  3. package/dist/components/registration-form.js +85 -32
  4. package/dist/components/registration-form.js.map +1 -1
  5. package/dist/components/registration-modal.js +85 -32
  6. package/dist/components/registration-modal.js.map +1 -1
  7. package/dist/index.js +85 -32
  8. package/dist/index.js.map +1 -1
  9. package/e2e/promo-code-date-filter.spec.js +103 -0
  10. package/e2e/promo-code-discovery.spec.js +55 -7
  11. package/e2e/promo-code-invalid-clears-warning.spec.js +57 -0
  12. package/package.json +3 -3
  13. package/.claude/rules/summit-registration-lite-component-props.md +0 -95
  14. package/.claude/rules/summit-registration-lite-i18n.md +0 -62
  15. package/.claude/rules/summit-registration-lite-payment-providers.md +0 -69
  16. package/.claude/rules/summit-registration-lite-project.md +0 -80
  17. package/.claude/rules/summit-registration-lite-redux-actions.md +0 -65
  18. package/.claude/rules/summit-registration-lite-step-flow.md +0 -77
  19. package/.claude/rules/summit-registration-lite-testing.md +0 -97
  20. package/.claude/rules/summit-registration-lite-utils.md +0 -71
  21. package/.claude/skills/summit-registration-lite-add-provider/SKILL.md +0 -155
  22. package/.claude/skills/summit-registration-lite-dev-setup/SKILL.md +0 -67
  23. package/.claude/skills/summit-registration-lite-publish/SKILL.md +0 -64
  24. package/.claude/skills/summit-registration-lite-scaffold-component/SKILL.md +0 -152
  25. package/.codegraph/config.json +0 -141
  26. package/.codegraph/daemon.pid +0 -6
@@ -1,77 +0,0 @@
1
- # Multi-Step Registration Flow
2
-
3
- ## When to Apply
4
-
5
- Adding step-specific logic, modifying step transitions, or rendering step-dependent UI.
6
-
7
- ## Step Constants
8
-
9
- Defined in `src/utils/constants.js`:
10
-
11
- ```javascript
12
- export const STEP_SELECT_TICKET_TYPE = 0; // Ticket selection + promo code
13
- export const STEP_PERSONAL_INFO = 1; // Attendee details + ticket assignment
14
- export const STEP_PAYMENT = 2; // Payment form (Stripe/LawPay)
15
- export const STEP_COMPLETE = 3; // Order confirmation
16
- ```
17
-
18
- ## Step Transitions
19
-
20
- **Forward flow (happy path):**
21
-
22
- ```
23
- STEP_SELECT_TICKET_TYPE → (Next/validate promo) → STEP_PERSONAL_INFO
24
- STEP_PERSONAL_INFO → (submit form) → reserveTicket → STEP_PAYMENT
25
- STEP_PAYMENT → (pay) → payTicketWithProvider → STEP_COMPLETE
26
- ```
27
-
28
- **Special cases:**
29
-
30
- | Scenario | Transition |
31
- |----------|------------|
32
- | Free ticket (cost === 0) | `STEP_PERSONAL_INFO` → skip payment → `STEP_COMPLETE` |
33
- | Pre-paid ticket | `STEP_PERSONAL_INFO` → skip payment → `STEP_COMPLETE` |
34
- | Payment failure | `STEP_PAYMENT` → `removeReservedTicket()` → `STEP_PERSONAL_INFO` |
35
- | No ticket selected + back | Any step → `STEP_SELECT_TICKET_TYPE` |
36
-
37
- **Back navigation:**
38
-
39
- - From `STEP_PERSONAL_INFO`: `changeStep(step - 1)`
40
- - From `STEP_PAYMENT`: `removeReservedTicket()` (clears reservation, reducer resets to `STEP_PERSONAL_INFO`)
41
-
42
- ## Redux Integration
43
-
44
- ```javascript
45
- // Action
46
- export const changeStep = (step) => (dispatch, getState) => {
47
- dispatch(createAction(CHANGE_STEP)(step));
48
- };
49
-
50
- // Reducer
51
- case CHANGE_STEP: {
52
- return { ...state, step: payload };
53
- }
54
- ```
55
-
56
- Default state: `step: STEP_SELECT_TICKET_TYPE`
57
-
58
- ## Rendering Pattern
59
-
60
- In `registration-lite.js`, each step renders conditionally:
61
-
62
- ```javascript
63
- <StepComponent isActive={step === STEP_SELECT_TICKET_TYPE} ... />
64
- <StepComponent isActive={step === STEP_PERSONAL_INFO} ... />
65
- <StepComponent isActive={step === STEP_PAYMENT} ... />
66
- {step === STEP_COMPLETE && <PurchaseCompleteComponent ... />}
67
- ```
68
-
69
- - Steps 0-2 share layout with `ButtonBarComponent`
70
- - Step 3 (complete) has its own layout, no button bar
71
- - Button bar renders step-specific buttons based on current `step` value
72
-
73
- ## Common Mistakes
74
-
75
- - Skipping `removeReservedTicket()` when going back from payment (leaves stale reservation)
76
- - Not checking `reservation` existence before allowing forward navigation
77
- - Adding steps without updating `ButtonBarComponent` (buttons won't render for new step)
@@ -1,97 +0,0 @@
1
- # Testing Patterns
2
-
3
- ## When to Apply
4
-
5
- Writing new component tests or maintaining existing tests.
6
-
7
- ## Pattern
8
-
9
- **Test Structure:**
10
-
11
- ```javascript
12
- import React from 'react';
13
- import { render, fireEvent, waitFor, screen } from '@testing-library/react';
14
- import '@testing-library/jest-dom';
15
- import ComponentName from '..';
16
-
17
- const mockCallback = jest.fn();
18
-
19
- afterEach(() => {
20
- jest.clearAllMocks(); // Reset mocks between tests
21
- });
22
-
23
- it('describes expected behavior', () => {
24
- const { getByTestId, getAllByTestId, queryByText } = render(
25
- <ComponentName prop1={value1} callback={mockCallback} />
26
- );
27
-
28
- // Arrange: find elements
29
- const button = getByTestId('button-id');
30
-
31
- // Act: interact
32
- fireEvent.click(button);
33
-
34
- // Assert: verify outcome
35
- expect(mockCallback).toHaveBeenCalledWith(expectedArg);
36
- });
37
- ```
38
-
39
- **Common Queries:**
40
-
41
- ```javascript
42
- // Single element (throws if not found)
43
- getByTestId('id') // data-testid attribute
44
- getByText('text') // visible text content
45
- getByRole('button') // ARIA role
46
-
47
- // Multiple elements
48
- getAllByTestId('id') // returns array
49
-
50
- // Nullable queries (returns null if not found)
51
- queryByText('text') // useful for "expect().toBeFalsy()"
52
-
53
- // From screen utility
54
- screen.queryByText('text')
55
- ```
56
-
57
- **Async Testing:**
58
-
59
- ```javascript
60
- it('handles async behavior', async () => {
61
- const { getByTestId } = render(<Component />);
62
-
63
- fireEvent.click(getByTestId('async-button'));
64
-
65
- await waitFor(() => {
66
- expect(getByTestId('result')).toBeInTheDocument();
67
- });
68
- });
69
- ```
70
-
71
- ## Key Points
72
-
73
- - Tests live in `__tests__/` directories alongside components
74
- - Use `data-testid` attributes for reliable element selection
75
- - Mock callbacks with `jest.fn()`, verify with `.toHaveBeenCalled()` or `.toHaveBeenCalledWith()`
76
- - Use `queryBy*` for negative assertions (`expect().toBeFalsy()`)
77
- - Use `getBy*` for positive assertions (throws if element missing)
78
- - `@testing-library/react` auto-cleanup enabled (no manual unmount needed)
79
- - CSS/SCSS mocked via `identity-obj-proxy` (className checks work)
80
- - Static assets (images) mocked via `src/__mocks__/fileMock.js`
81
-
82
- ## Test Organization
83
-
84
- ```
85
- src/components/my-component/
86
- ├── index.js
87
- ├── my-component.module.scss
88
- └── __tests__/
89
- └── my-component.test.js
90
- ```
91
-
92
- ## Common Mistakes
93
-
94
- - Using brittle selectors (text content that changes) instead of `data-testid`
95
- - Forgetting `@testing-library/jest-dom` import (causes `.toBeInTheDocument()` to fail)
96
- - Not awaiting async operations (tests pass locally, fail in CI due to timing)
97
- - Testing implementation details instead of user behavior (prefer user interactions over internal state checks)
@@ -1,71 +0,0 @@
1
- # Utility Functions
2
-
3
- ## When to Apply
4
-
5
- Working with ticket pricing, order status checks, payment provider resolution, or UI color logic.
6
-
7
- ## Key Functions (`src/utils/utils.js`)
8
-
9
- ### Order/Ticket Status Checks
10
-
11
- ```javascript
12
- isFreeOrder(reservation) // reservation.amount === 0
13
- isPrePaidOrder(reservation) // status === 'Paid' && payment_method === 'Offline'
14
- isPrePaidTicketType(ticketType) // ticketType.sub_type === 'PrePaid'
15
- hasDiscountApplied(ticketType) // has cost_with_applied_discount !== cost
16
- ```
17
-
18
- Used in payment providers to decide whether to skip the payment gateway.
19
-
20
- ### Pricing Display
21
-
22
- ```javascript
23
- getTicketCost(ticket, quantity) // Returns JSX: strikethrough original + discounted price, or just price
24
- getTicketTaxes(ticket, taxes) // Returns tax label string: " plus Tax A & Tax B"
25
- formatCurrency(amount, { currency }) // From helpers/ — locale-aware currency formatting
26
- ```
27
-
28
- ### Payment Provider Resolution
29
-
30
- ```javascript
31
- getCurrentProvider(summit)
32
- // Iterates summit.payment_profiles for application_type === 'Registration'
33
- // Returns { publicKey, provider } — uses test vs live key based on test_mode_enabled
34
- ```
35
-
36
- ### UI Helpers
37
-
38
- ```javascript
39
- getContrastingTextColor(bgColor, lightColor, darkColor) // WCAG luminance contrast
40
- parseColor(input, element) // Resolves CSS variables to RGB
41
- removeWhiteSpaces(value) // Strip all whitespace
42
- isEmptyString(val) // Trim-aware empty check
43
- ```
44
-
45
- ### Error Tracking
46
-
47
- ```javascript
48
- handleSentryException(err) // Sends to Sentry if window.SENTRY_DSN is set, else console.log
49
- ```
50
-
51
- ### Analytics
52
-
53
- ```javascript
54
- buildTrackEvent(data, ticketQuantity, promoCode)
55
- // Builds GA4-style event data with currency, items_array, discount
56
- ```
57
-
58
- ## Helper Exports (`src/helpers/`)
59
-
60
- Barrel export from `src/helpers/index.js`:
61
-
62
- - `capitalizeFirstLetter` — text formatting
63
- - `formatCurrency` — locale-aware currency display
64
- - `formatErrorMessage` — error message normalization
65
- - `getTicketMaxQuantity` — ticket quantity constraints
66
-
67
- ## Common Mistakes
68
-
69
- - Using `reservation.amount === 0` directly instead of `isFreeOrder()` (misses edge cases)
70
- - Forgetting `isPrePaidOrder()` check alongside `isFreeOrder()` in payment flows
71
- - Not handling `getCurrentProvider()` returning `{ publicKey: null, provider: '' }` when no payment profile exists
@@ -1,155 +0,0 @@
1
- ---
2
- name: summit-registration-lite-add-provider
3
- version: 1.0.0
4
- description: Scaffold new payment provider with factory registration
5
- triggers:
6
- - add payment provider
7
- - new payment gateway
8
- - integrate payment
9
- ---
10
-
11
- # Add Payment Provider Workflow
12
-
13
- Scaffolds a new payment provider class with factory registration, error handling, and test template.
14
-
15
- ## Steps
16
-
17
- 1. **Ask for provider details**
18
- - Provider name (e.g., "PayPal", "Square")
19
- - Provider constant (e.g., "PAYMENT_PROVIDER_PAYPAL")
20
- - Gateway-specific fields needed (API keys, client IDs, etc.)
21
-
22
- 2. **Add constant to utils/constants.js**
23
- ```javascript
24
- export const PAYMENT_PROVIDER_NEWNAME = 'new_provider_name';
25
- ```
26
-
27
- 3. **Create provider class**
28
- File: `src/utils/payment-providers/{name}-provider.js`
29
-
30
- Use StripeProvider as template:
31
- - Constructor receives: `{ reservation, summitId, userProfile, access_token, apiBaseUrl, dispatch }`
32
- - Implement `payTicket` method with signature:
33
- ```javascript
34
- payTicket = ({ /* gateway-specific params */, onError = () => {} }) => async (dispatch) => {
35
- // Implementation
36
- }
37
- ```
38
- - Handle free/prepaid orders (skip gateway, call `/checkout` directly)
39
- - Custom error handler for HTTP status codes (404 → VALIDATION, 500 → ERROR)
40
- - Success flow: checkout → CLEAR_RESERVATION → changeStep(STEP_COMPLETE)
41
- - Failure flow: removeReservedTicket → changeStep(STEP_PERSONAL_INFO)
42
-
43
- 4. **Register in factory**
44
- File: `src/utils/payment-providers/payment-provider-factory.js`
45
-
46
- Add import and case:
47
- ```javascript
48
- import { NewProvider } from './new-provider';
49
-
50
- case PAYMENT_PROVIDER_NEWNAME: {
51
- currentProvider = new NewProvider({...params});
52
- break;
53
- }
54
- ```
55
-
56
- 5. **Add provider component** (if custom UI needed)
57
- File: `src/components/{name}-component/index.js`
58
-
59
- Handles gateway-specific UI (payment form, buttons, etc.).
60
-
61
- 6. **Create test file**
62
- File: `src/utils/payment-providers/__tests__/{name}-provider.test.js`
63
-
64
- Mock patterns:
65
- - Mock API calls (putRequest)
66
- - Mock gateway SDK (if any)
67
- - Test free/prepaid flow
68
- - Test error handling
69
-
70
- 7. **Update PaymentComponent**
71
- File: `src/components/payment/index.js`
72
-
73
- Add conditional rendering for new provider UI.
74
-
75
- ## Provider Template
76
-
77
- ```javascript
78
- import { createAction, putRequest, authErrorHandler } from 'openstack-uicore-foundation/lib/utils/actions';
79
- import { CLEAR_RESERVATION, PAY_RESERVATION } from '../../actions';
80
- import { changeStep, removeReservedTicket, startWidgetLoading, stopWidgetLoading } from '../../actions';
81
- import { isFreeOrder, isPrePaidOrder } from '../utils';
82
- import { ERROR_TYPE_ERROR, ERROR_TYPE_VALIDATION, STEP_COMPLETE, STEP_PERSONAL_INFO } from '../constants';
83
-
84
- export class NewProvider {
85
- constructor({ reservation, summitId, userProfile, access_token, apiBaseUrl, dispatch }) {
86
- this.reservation = reservation;
87
- this.summitId = summitId;
88
- this.userProfile = userProfile;
89
- this.access_token = access_token;
90
- this.apiBaseUrl = apiBaseUrl;
91
- this.dispatch = dispatch;
92
- }
93
-
94
- payTicket = ({ /* custom params */, onError = () => {} }) => async (dispatch) => {
95
- const errorHandler = (err, res) => (dispatch, state) => {
96
- let code = err.status;
97
- switch (code) {
98
- case 404: onError({type: ERROR_TYPE_VALIDATION, msg: res.body.message, exception: null}); break;
99
- case 500: onError({type: ERROR_TYPE_ERROR, msg: res.body.message, exception: null}); break;
100
- default: authErrorHandler(err, res)(dispatch, state); break;
101
- }
102
- };
103
-
104
- const normalizedEntity = {
105
- billing_address_1: this.userProfile?.address1 || '',
106
- billing_address_2: this.userProfile?.address2 || '',
107
- billing_address_city: this.userProfile?.locality || '',
108
- billing_address_state: this.userProfile?.region || '',
109
- billing_address_country: this.userProfile?.country || ''
110
- };
111
-
112
- dispatch(startWidgetLoading());
113
-
114
- // Free/prepaid orders
115
- if (isFreeOrder(this.reservation) || isPrePaidOrder(this.reservation)) {
116
- return this.checkout(normalizedEntity, errorHandler, onError);
117
- }
118
-
119
- // Gateway-specific payment flow here
120
- // Then call this.checkout() on success
121
- };
122
-
123
- checkout = (normalizedEntity, errorHandler, onError) => {
124
- const params = { access_token: this.access_token, expand: 'tickets,tickets.owner,...' };
125
-
126
- return putRequest(
127
- null,
128
- createAction(PAY_RESERVATION),
129
- `${this.apiBaseUrl}/api/v1/summits/${this.summitId}/orders/${this.reservation.hash}/checkout`,
130
- normalizedEntity,
131
- errorHandler
132
- )(params)(this.dispatch)
133
- .then((payload) => {
134
- this.dispatch(stopWidgetLoading());
135
- this.dispatch(createAction(CLEAR_RESERVATION)({}));
136
- this.dispatch(changeStep(STEP_COMPLETE));
137
- return payload;
138
- })
139
- .catch(e => {
140
- this.dispatch(stopWidgetLoading());
141
- this.dispatch(removeReservedTicket());
142
- this.dispatch(changeStep(STEP_PERSONAL_INFO));
143
- onError({type: ERROR_TYPE_ERROR, msg: e?.message, exception: e});
144
- return e;
145
- });
146
- };
147
- }
148
- ```
149
-
150
- ## Testing
151
-
152
- Run provider tests:
153
- ```bash
154
- yarn test payment-providers
155
- ```
@@ -1,67 +0,0 @@
1
- ---
2
- name: summit-registration-lite-dev-setup
3
- version: 1.0.0
4
- description: Clean install and start development server
5
- triggers:
6
- - setup dev environment
7
- - start dev server
8
- - clean install
9
- ---
10
-
11
- # Development Setup Workflow
12
-
13
- Cleans existing installation and starts fresh development environment.
14
-
15
- ## Steps
16
-
17
- ### Option 1: Clean Setup (Recommended for Issues)
18
-
19
- 1. **Clean all build artifacts and dependencies**
20
- ```bash
21
- yarn clean
22
- ```
23
- Removes `dist/` and `node_modules/`, then reinstalls with `yarn`.
24
-
25
- 2. **Start dev server**
26
- ```bash
27
- yarn serve
28
- ```
29
- Starts webpack-dev-server on default port (usually 8080). Opens browser automatically.
30
-
31
- ### Option 2: Quick Start (No Clean)
32
-
33
- 1. **Install dependencies** (if `node_modules/` missing)
34
- ```bash
35
- yarn
36
- ```
37
-
38
- 2. **Start dev server**
39
- ```bash
40
- yarn serve
41
- ```
42
-
43
- ## Development Configuration
44
-
45
- - **Webpack config:** `webpack.dev.js` (extends `webpack.common.js`)
46
- - **Environment variables:**
47
- - `API_BASE_URL` - API endpoint for development
48
- - `ACCESS_TOKEN` - Auth token for standalone testing (stored in localStorage)
49
-
50
- Set these in shell or `.env` file:
51
- ```bash
52
- export API_BASE_URL=https://api.dev.example.com
53
- export ACCESS_TOKEN=your_dev_token
54
- ```
55
-
56
- ## Standalone Testing
57
-
58
- The widget supports standalone mode in development. Set environment variables before running `yarn serve` to test without parent app.
59
-
60
- ## Troubleshooting
61
-
62
- | Issue | Solution |
63
- |-------|----------|
64
- | Port already in use | Kill process: `lsof -ti:8080 | xargs kill` or change port in webpack.dev.js |
65
- | Stale cache | Run `yarn clean` |
66
- | Module not found | Delete `node_modules/` and `yarn.lock`, then `yarn` |
67
- | Hot reload not working | Check webpack-dev-server config, ensure `watchFiles` configured |
@@ -1,64 +0,0 @@
1
- ---
2
- name: summit-registration-lite-publish
3
- version: 1.0.0
4
- description: Build and publish package to NPM registry
5
- triggers:
6
- - publish to npm
7
- - release package
8
- - deploy widget
9
- ---
10
-
11
- # Package Publishing Workflow
12
-
13
- Builds the production bundle and publishes to NPM registry.
14
-
15
- ## Steps
16
-
17
- 1. **Verify clean state**
18
- ```bash
19
- git status
20
- ```
21
- Ensure no uncommitted changes. If changes exist, ask user to commit or stash first.
22
-
23
- 2. **Verify version**
24
- ```bash
25
- cat package.json | grep '"version"'
26
- ```
27
- Confirm version with user before publishing. Ask if version should be bumped (patch/minor/major).
28
-
29
- 3. **Build production bundle**
30
- ```bash
31
- yarn build
32
- ```
33
- Compiles with webpack.prod.js. Output goes to `dist/`.
34
-
35
- 4. **Verify build output**
36
- ```bash
37
- ls -lh dist/
38
- ```
39
- Check that `index.js` and `index.css` exist.
40
-
41
- 5. **Publish to NPM**
42
- ```bash
43
- yarn publish
44
- ```
45
- Prompts for new version (if not already updated). Requires NPM authentication.
46
-
47
- ## Alternative: Combined Command
48
-
49
- The `yarn publish-package` script combines build and publish:
50
- ```bash
51
- yarn publish-package
52
- ```
53
-
54
- ## Prerequisites
55
-
56
- - NPM authentication configured (`npm login`)
57
- - Write access to `summit-registration-lite` package on NPM
58
- - Clean git working directory (or committed changes)
59
-
60
- ## Post-Publish
61
-
62
- - Tag the release in git: `git tag v{version}`
63
- - Push tag: `git push origin v{version}`
64
- - Update CHANGELOG.md with release notes
@@ -1,152 +0,0 @@
1
- ---
2
- name: summit-registration-lite-scaffold-component
3
- version: 1.0.0
4
- description: Create new component with folder structure, test file, and SCSS module
5
- triggers:
6
- - create new component
7
- - scaffold component
8
- - add component
9
- ---
10
-
11
- # Component Scaffolding Workflow
12
-
13
- Creates a new React component with proper folder structure, SCSS module, and test boilerplate.
14
-
15
- ## Steps
16
-
17
- 1. **Ask for component details**
18
- - Component name (PascalCase, e.g., "TicketSelector")
19
- - Purpose/description
20
- - Key props (if known)
21
-
22
- 2. **Create directory structure**
23
- ```
24
- src/components/{component-name}/
25
- ├── index.js
26
- ├── {component-name}.module.scss
27
- └── __tests__/
28
- └── {component-name}.test.js
29
- ```
30
-
31
- 3. **Generate component file**
32
- File: `src/components/{component-name}/index.js`
33
-
34
- Template:
35
- ```javascript
36
- import React from 'react';
37
- import PropTypes from 'prop-types';
38
- import styles from './{component-name}.module.scss';
39
-
40
- const ComponentName = ({ prop1, prop2 }) => {
41
- return (
42
- <div className={styles.container}>
43
- {/* Component content */}
44
- </div>
45
- );
46
- };
47
-
48
- ComponentName.propTypes = {
49
- prop1: PropTypes.string.isRequired,
50
- prop2: PropTypes.func,
51
- };
52
-
53
- ComponentName.defaultProps = {
54
- prop2: () => {},
55
- };
56
-
57
- export default ComponentName;
58
- ```
59
-
60
- 4. **Generate SCSS module**
61
- File: `src/components/{component-name}/{component-name}.module.scss`
62
-
63
- Template:
64
- ```scss
65
- .container {
66
- // Component styles
67
- }
68
- ```
69
-
70
- 5. **Generate test file**
71
- File: `src/components/{component-name}/__tests__/{component-name}.test.js`
72
-
73
- Template:
74
- ```javascript
75
- import React from 'react';
76
- import { render, fireEvent, screen } from '@testing-library/react';
77
- import '@testing-library/jest-dom';
78
- import ComponentName from '..';
79
-
80
- const mockCallback = jest.fn();
81
-
82
- afterEach(() => {
83
- jest.clearAllMocks();
84
- });
85
-
86
- describe('ComponentName', () => {
87
- it('renders without crashing', () => {
88
- const { getByTestId } = render(
89
- <ComponentName prop1="test" prop2={mockCallback} />
90
- );
91
- expect(getByTestId('component-container')).toBeInTheDocument();
92
- });
93
-
94
- it('calls callback on interaction', () => {
95
- const { getByTestId } = render(
96
- <ComponentName prop1="test" prop2={mockCallback} />
97
- );
98
- fireEvent.click(getByTestId('test-button'));
99
- expect(mockCallback).toHaveBeenCalled();
100
- });
101
- });
102
- ```
103
-
104
- 6. **Add data-testid attributes**
105
- Remind user to add `data-testid` attributes to key elements for testing.
106
-
107
- ## Component Conventions
108
-
109
- - **Props:** Use PropTypes for validation, provide defaultProps for optional props
110
- - **Styles:** Use CSS modules (`.module.scss`) for scoped styles
111
- - **Testing:** Use `data-testid` for reliable element selection
112
- - **Exports:** Default export for component, named exports for sub-components/utils if needed
113
-
114
- ## Redux-Connected Components
115
-
116
- For components that need Redux state, use `connect`:
117
-
118
- ```javascript
119
- import { connect } from 'react-redux';
120
- import { someAction } from '../../actions';
121
-
122
- const ComponentName = ({ stateValue, someAction }) => {
123
- // Component implementation
124
- };
125
-
126
- const mapStateToProps = ({ registration }) => ({
127
- stateValue: registration.stateValue,
128
- });
129
-
130
- export default connect(mapStateToProps, { someAction })(ComponentName);
131
- ```
132
-
133
- ## Testing Redux Components
134
-
135
- Wrap in Provider for testing:
136
- ```javascript
137
- import { Provider } from 'react-redux';
138
- import { getStore } from '../../store';
139
-
140
- const renderWithStore = (component) => {
141
- const store = getStore('test-client', 'http://api.test', () => 'mock-token');
142
- return render(<Provider store={store}>{component}</Provider>);
143
- };
144
- ```
145
-
146
- ## Next Steps After Scaffolding
147
-
148
- 1. Implement component logic
149
- 2. Add styles
150
- 3. Write tests (aim for key interactions)
151
- 4. Import and use in parent component
152
- 5. Verify in dev server