@cobwds/mcp-server 1.19.3 → 1.23.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 +6 -15
- package/data/cem/custom-elements.json +10084 -6696
- package/data/frameworks.json +14 -0
- package/data/guides/form-validation.md +1 -1
- package/data/guides/frameworks/angular.md +184 -26
- package/data/guides/frameworks/react-additions.md +69 -0
- package/data/guides/frameworks/react.md +209 -27
- package/data/guides/frameworks/vue.md +356 -0
- package/data/tokens/tokens.css +45 -56
- package/data/tokens/tokens.json +39 -78
- package/dist/index.js +0 -0
- package/dist/lib/cem-parser.d.ts +12 -0
- package/dist/lib/cem-parser.d.ts.map +1 -1
- package/dist/lib/framework-snippets.d.ts +36 -0
- package/dist/lib/framework-snippets.d.ts.map +1 -0
- package/dist/lib/framework-snippets.js +55 -0
- package/dist/lib/framework-usage.d.ts +20 -0
- package/dist/lib/framework-usage.d.ts.map +1 -0
- package/dist/lib/framework-usage.js +50 -0
- package/dist/lib/frameworks-data.d.ts +19 -0
- package/dist/lib/frameworks-data.d.ts.map +1 -0
- package/dist/lib/frameworks-data.js +40 -0
- package/dist/resources/frameworks.d.ts +15 -0
- package/dist/resources/frameworks.d.ts.map +1 -0
- package/dist/resources/frameworks.js +104 -0
- package/dist/resources/guides.d.ts.map +1 -1
- package/dist/resources/guides.js +12 -1
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/index.js +2 -0
- package/dist/tools/component-tools.d.ts.map +1 -1
- package/dist/tools/component-tools.js +20 -5
- package/dist/tools/guide-tools.js +1 -1
- package/dist/tools/utility-tools.d.ts +48 -0
- package/dist/tools/utility-tools.d.ts.map +1 -1
- package/dist/tools/utility-tools.js +236 -71
- package/package.json +6 -4
|
@@ -165,5 +165,5 @@ The following COBWDS components support `showError` and `errorMessage`:
|
|
|
165
165
|
|
|
166
166
|
1. **Error summary links**: Include anchor links to fields for keyboard navigation
|
|
167
167
|
2. **Focus management**: Move focus to error summary or first error field on validation failure
|
|
168
|
-
3. **ARIA attributes**: COBWDS components automatically set `aria-invalid` and `aria-
|
|
168
|
+
3. **ARIA attributes**: COBWDS form components automatically set `aria-invalid` on the control and associate the error message via `aria-errormessage`, and associate the visible `<cob-label>` with the control via `aria-labelledby` — no manual wiring needed
|
|
169
169
|
4. **Clear error messages**: Use specific, actionable error text
|
|
@@ -1,16 +1,32 @@
|
|
|
1
|
-
|
|
1
|
+
<!--
|
|
2
|
+
GENERATED FILE. Do not edit directly.
|
|
3
|
+
Source: packages/angular/README.md
|
|
4
|
+
Regenerate with: npm run sync:guides -w @cobwds/mcp-server
|
|
5
|
+
-->
|
|
6
|
+
# @cobwds/angular
|
|
2
7
|
|
|
3
|
-
|
|
8
|
+
Angular components for the City of Boston Web Design System (COBWDS). Generated from the COBWDS web components library, with full support for template-driven forms, Reactive Forms, and Signal Forms.
|
|
4
9
|
|
|
5
|
-
##
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
### Setup a new Angular project
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g @angular/cli
|
|
16
|
+
ng new my-angular-app
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Install NYS Design System
|
|
6
20
|
|
|
7
21
|
```bash
|
|
8
|
-
npm install @cobwds/
|
|
22
|
+
npm install @cobwds/angular @cobwds/styles
|
|
9
23
|
```
|
|
10
24
|
|
|
25
|
+
Peer dependencies: Angular `>=20.0.0` (core, common, forms).
|
|
26
|
+
|
|
11
27
|
## Load styles
|
|
12
28
|
|
|
13
|
-
Add
|
|
29
|
+
Add COBWDS styles to your `angular.json`:
|
|
14
30
|
|
|
15
31
|
```json
|
|
16
32
|
{
|
|
@@ -20,8 +36,7 @@ Add the COBWDS stylesheet to `angular.json`:
|
|
|
20
36
|
"build": {
|
|
21
37
|
"options": {
|
|
22
38
|
"styles": [
|
|
23
|
-
"node_modules/@cobwds/styles/
|
|
24
|
-
"src/styles.css"
|
|
39
|
+
"node_modules/@cobwds/styles/cobwds-full.min.css"
|
|
25
40
|
]
|
|
26
41
|
}
|
|
27
42
|
}
|
|
@@ -31,41 +46,184 @@ Add the COBWDS stylesheet to `angular.json`:
|
|
|
31
46
|
}
|
|
32
47
|
```
|
|
33
48
|
|
|
34
|
-
|
|
49
|
+
## First component
|
|
50
|
+
|
|
51
|
+
Import standalone components directly into your component:
|
|
35
52
|
|
|
36
|
-
|
|
53
|
+
```typescript
|
|
54
|
+
import { Component, signal } from '@angular/core';
|
|
55
|
+
import { RouterOutlet } from '@angular/router';
|
|
56
|
+
import { CobButtonComponent } from '@cobwds/angular';
|
|
57
|
+
@Component({
|
|
58
|
+
imports: [RouterOutlet, CobButtonComponent],
|
|
59
|
+
selector: 'app-root',
|
|
60
|
+
styleUrl: './app.css',
|
|
61
|
+
templateUrl: './app.html',
|
|
62
|
+
})
|
|
63
|
+
export class App {
|
|
64
|
+
protected readonly title = signal('my-angular-app');
|
|
65
|
+
handleSubmit() {
|
|
66
|
+
console.log('Button clicked!');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## NgModule support has been deprecated
|
|
72
|
+
|
|
73
|
+
Use "Standalone Components" instead.
|
|
74
|
+
|
|
75
|
+
## Inputs, outputs, and two-way binding
|
|
76
|
+
|
|
77
|
+
All properties are typed inputs; events are typed outputs. No `CUSTOM_ELEMENTS_SCHEMA` needed:
|
|
78
|
+
|
|
79
|
+
```html
|
|
80
|
+
<cob-textinput
|
|
81
|
+
[label]="'Email'"
|
|
82
|
+
[required]="true"
|
|
83
|
+
(nysChange)="onEmailChange($event)"
|
|
84
|
+
></cob-textinput>
|
|
85
|
+
```
|
|
37
86
|
|
|
38
|
-
|
|
87
|
+
Event detail is typed:
|
|
39
88
|
|
|
40
89
|
```typescript
|
|
41
|
-
|
|
90
|
+
onEmailChange(event: CobTextinputChangeEvent) {
|
|
91
|
+
console.log(event.detail.value); // autocompletes
|
|
92
|
+
}
|
|
93
|
+
```
|
|
42
94
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
95
|
+
## Signal Forms Known Limitations
|
|
96
|
+
|
|
97
|
+
When using Angular's Signal Forms with `[formField]`, be aware of these constraints:
|
|
98
|
+
|
|
99
|
+
- **name property overwrite**: The `[formField]` binding overwrites the `name` attribute. This is an Angular Signal Forms limitation; there is no workaround on the component side. Use a different approach for form arrays that require distinct names.
|
|
100
|
+
- **pattern validation on empty array**: Angular's Signal Forms passes an empty array for unset pattern metadata. The component defensively ignores these writes; no action needed on your side.
|
|
101
|
+
|
|
102
|
+
## Forms
|
|
103
|
+
|
|
104
|
+
### Template-driven forms
|
|
105
|
+
|
|
106
|
+
Use `[(ngModel)]` with form components:
|
|
107
|
+
|
|
108
|
+
```html
|
|
109
|
+
<form>
|
|
110
|
+
<cob-textinput [(ngModel)]="email" name="email"></cob-textinput>
|
|
111
|
+
<cob-checkbox [(ngModel)]="agreed" name="agreed"></cob-checkbox>
|
|
112
|
+
</form>
|
|
47
113
|
```
|
|
48
114
|
|
|
49
|
-
|
|
115
|
+
### Reactive Forms
|
|
116
|
+
|
|
117
|
+
Use `formControl` or `formControlName`:
|
|
50
118
|
|
|
51
119
|
```typescript
|
|
52
|
-
|
|
120
|
+
form = this.fb.group({
|
|
121
|
+
email: ["", [Validators.required, Validators.email]],
|
|
122
|
+
agreed: [false, Validators.required],
|
|
123
|
+
});
|
|
124
|
+
```
|
|
53
125
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
126
|
+
```html
|
|
127
|
+
<form [formGroup]="form">
|
|
128
|
+
<cob-textinput formControlName="email"></cob-textinput>
|
|
129
|
+
<cob-checkbox formControlName="agreed"></cob-checkbox>
|
|
130
|
+
</form>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Signal Forms (Angular 21+)
|
|
134
|
+
|
|
135
|
+
Use `[formField]` with a form field definition:
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
email = signal<string>("");
|
|
139
|
+
emailControl = new FormControl<string>("", Validators.required);
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```html
|
|
143
|
+
<cob-textinput [formField]="emailControl"></cob-textinput>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Group controls
|
|
147
|
+
|
|
148
|
+
`cob-checkboxgroup` and `cob-radiogroup` bind at group level:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
form = this.fb.group({
|
|
152
|
+
languages: [["en"], Validators.required],
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
```html
|
|
157
|
+
<cob-checkboxgroup formControlName="languages">
|
|
158
|
+
<cob-checkbox value="en">English</cob-checkbox>
|
|
159
|
+
<cob-checkbox value="es">Spanish</cob-checkbox>
|
|
160
|
+
</cob-checkboxgroup>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Validation
|
|
164
|
+
|
|
165
|
+
By default, the component owns validation. Its `required` and `pattern` attributes drive ElementInternals validation, shown on blur.
|
|
166
|
+
|
|
167
|
+
To let Angular own validation instead, add the `nysControlErrors` directive:
|
|
168
|
+
|
|
169
|
+
```html
|
|
170
|
+
<cob-textinput
|
|
171
|
+
formControlName="email"
|
|
172
|
+
nysControlErrors
|
|
173
|
+
></cob-textinput>
|
|
59
174
|
```
|
|
60
175
|
|
|
61
|
-
|
|
176
|
+
The directive subscribes to `control.errors` and sets the component's `showError` and `errorMessage`. Override the default messages:
|
|
62
177
|
|
|
63
|
-
|
|
178
|
+
```typescript
|
|
179
|
+
providers: [
|
|
180
|
+
{
|
|
181
|
+
provide: NYS_ERROR_MESSAGES,
|
|
182
|
+
useValue: {
|
|
183
|
+
required: () => "Please fill in this field",
|
|
184
|
+
email: () => "Enter a valid email",
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
]
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Disabled state
|
|
191
|
+
|
|
192
|
+
`FormControl({ disabled: true })` disables the component:
|
|
64
193
|
|
|
65
194
|
```typescript
|
|
66
|
-
|
|
195
|
+
control = new FormControl({ value: "", disabled: true });
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
```html
|
|
199
|
+
<cob-textinput [formControl]="control"></cob-textinput>
|
|
67
200
|
```
|
|
68
201
|
|
|
202
|
+
## Server-side rendering
|
|
203
|
+
|
|
204
|
+
The components render client-side. For SSR, wrap containers in `ngSkipHydration`:
|
|
205
|
+
|
|
69
206
|
```html
|
|
70
|
-
<
|
|
207
|
+
<div ngSkipHydration>
|
|
208
|
+
<cob-textinput></cob-textinput>
|
|
209
|
+
</div>
|
|
71
210
|
```
|
|
211
|
+
|
|
212
|
+
Or use `provideClientHydration` if registering components client-only.
|
|
213
|
+
|
|
214
|
+
## Zoneless
|
|
215
|
+
|
|
216
|
+
The component outputs use Angular's `output()` function, which integrates with zoneless change detection. Zoneless apps work without extra configuration.
|
|
217
|
+
|
|
218
|
+
## Subpath imports
|
|
219
|
+
|
|
220
|
+
Import individual components:
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import { CobTextinputComponent } from "@cobwds/angular/textinput";
|
|
224
|
+
import { CobCheckboxComponent } from "@cobwds/angular/checkbox";
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## License
|
|
228
|
+
|
|
229
|
+
MIT
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
## Using the stepper in React (multi-page pattern)
|
|
2
|
+
|
|
3
|
+
`cob-stepper` requires careful state management in React. Two state variables are needed:
|
|
4
|
+
|
|
5
|
+
- `currentStep` — the furthest step the user has reached (progress boundary). Maps to the `current` prop on `CobStep`.
|
|
6
|
+
- `viewingStep` — which step's content is currently displayed. Maps to the `selected` prop on `CobStep`.
|
|
7
|
+
|
|
8
|
+
**Always pass both `current` and `selected` explicitly from React state.** Without explicit `selected`, the stepper's internal fallback will override the user's sidebar navigation on every React re-render.
|
|
9
|
+
|
|
10
|
+
**Event listening:** Use `onCobStepClick` on each `CobStep` (not on `CobStepper`). Always call `e.preventDefault()` to suppress `href`-based page navigation.
|
|
11
|
+
|
|
12
|
+
**Do not use `href` for SPA routing.** Omit `href` and handle step changes entirely in React state.
|
|
13
|
+
|
|
14
|
+
**Do not place `<CobStepper>` inside a `<form>` element.**
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { useState } from 'react';
|
|
18
|
+
import { CobStepper, CobStep, CobButton } from '@cobwds/react';
|
|
19
|
+
|
|
20
|
+
const STEPS = [
|
|
21
|
+
{ label: 'Personal Info', content: <PersonalInfoForm /> },
|
|
22
|
+
{ label: 'Contact Details', content: <ContactForm /> },
|
|
23
|
+
{ label: 'Review', content: <ReviewForm /> },
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
export function MultiStepForm() {
|
|
27
|
+
const [currentStep, setCurrentStep] = useState(0);
|
|
28
|
+
const [viewingStep, setViewingStep] = useState(0);
|
|
29
|
+
|
|
30
|
+
function handleContinue() {
|
|
31
|
+
if (viewingStep < STEPS.length - 1) {
|
|
32
|
+
setCurrentStep(viewingStep + 1);
|
|
33
|
+
setViewingStep(viewingStep + 1);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div className="cob-grid-row">
|
|
39
|
+
<CobStepper label="Application" className="cob-grid-col-12 cob-desktop:cob-grid-col-3">
|
|
40
|
+
{STEPS.map((step, i) => (
|
|
41
|
+
<CobStep
|
|
42
|
+
key={i}
|
|
43
|
+
label={step.label}
|
|
44
|
+
current={i === currentStep}
|
|
45
|
+
selected={i === viewingStep}
|
|
46
|
+
onCobStepClick={(e) => { e.preventDefault(); setViewingStep(i); }}
|
|
47
|
+
/>
|
|
48
|
+
))}
|
|
49
|
+
<div slot="actions">
|
|
50
|
+
<CobButton label="Save & Exit" variant="outline" fullWidth />
|
|
51
|
+
</div>
|
|
52
|
+
</CobStepper>
|
|
53
|
+
|
|
54
|
+
<main className="cob-grid-col-12 cob-desktop:cob-grid-col-9">
|
|
55
|
+
{STEPS[viewingStep].content}
|
|
56
|
+
<div style={{ display: 'flex', gap: '8px', marginTop: '16px' }}>
|
|
57
|
+
{viewingStep > 0 && (
|
|
58
|
+
<CobButton label="Back" variant="outline"
|
|
59
|
+
onCobClick={() => setViewingStep((v) => v - 1)} />
|
|
60
|
+
)}
|
|
61
|
+
{viewingStep === currentStep && currentStep < STEPS.length - 1 && (
|
|
62
|
+
<CobButton label="Continue" onCobClick={handleContinue} />
|
|
63
|
+
)}
|
|
64
|
+
</div>
|
|
65
|
+
</main>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
```
|
|
@@ -1,48 +1,230 @@
|
|
|
1
|
-
|
|
1
|
+
<!--
|
|
2
|
+
GENERATED FILE. Do not edit directly.
|
|
3
|
+
Source: packages/react/README.md + data/guides/frameworks/react-additions.md
|
|
4
|
+
Regenerate with: npm run sync:guides -w @cobwds/mcp-server
|
|
5
|
+
-->
|
|
6
|
+
# @cobwds/react
|
|
2
7
|
|
|
3
|
-
|
|
8
|
+
React components for the City of Boston Web Design System (COBWDS). Generated from the COBWDS web components library.
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
Works with React 18 and React 19, including Next.js App Router.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
### Setup a new React project
|
|
6
15
|
|
|
7
16
|
```bash
|
|
8
|
-
npm
|
|
17
|
+
npm create vite@latest my-app -- --template react-ts
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Install City of Boston Web Design System
|
|
21
|
+
```sh
|
|
22
|
+
npm install @cobwds/react @cobwds/styles
|
|
9
23
|
```
|
|
10
24
|
|
|
11
25
|
## Load styles
|
|
12
26
|
|
|
13
|
-
Import the
|
|
27
|
+
Import the design system stylesheet once, in your app's main.tsx:
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import "@cobwds/styles/full";
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Without it, components render unstyled. `@cobwds/styles` ships the
|
|
34
|
+
design tokens and global styles; component-level styles live in each
|
|
35
|
+
component's shadow DOM and need no extra setup.
|
|
36
|
+
|
|
37
|
+
## First component
|
|
38
|
+
|
|
39
|
+
Edit your App.tsx file to import COBWDS components
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import { CobButton } from "@cobwds/react/button";
|
|
43
|
+
<CobButton label="Submit" variant="filled" />
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Props, events, refs
|
|
47
|
+
|
|
48
|
+
**Props** are typed from the underlying element class and set as DOM
|
|
49
|
+
properties, not attributes. Use the camelCase property names
|
|
50
|
+
(`showError`, `errorMessage`), not the kebab-case attribute names.
|
|
51
|
+
|
|
52
|
+
**Events** keep their full COBWDS names, mapped to `onCob*` props:
|
|
53
|
+
`cob-change` becomes `onCobChange`, `cob-input` becomes `onCobInput`.
|
|
54
|
+
The handler parameter is the typed event, so `e.detail` autocompletes:
|
|
14
55
|
|
|
15
56
|
```tsx
|
|
16
|
-
|
|
17
|
-
|
|
57
|
+
<CobTextinput
|
|
58
|
+
label="First name"
|
|
59
|
+
onCobInput={(e) => console.log(e.detail.value)}
|
|
60
|
+
/>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
There is no synthetic `onChange`. React's `onChange` prop attaches a
|
|
64
|
+
native `change` listener, which COBWDS components don't dispatch — use
|
|
65
|
+
`onCobChange`.
|
|
66
|
+
|
|
67
|
+
**Refs** resolve to the element instance:
|
|
18
68
|
|
|
19
|
-
|
|
20
|
-
|
|
69
|
+
```tsx
|
|
70
|
+
const ref = React.useRef<CobTextinputElement>(null);
|
|
71
|
+
<CobTextinput ref={ref} label="Name" />;
|
|
72
|
+
// ref.current?.checkValidity()
|
|
21
73
|
```
|
|
22
74
|
|
|
23
|
-
##
|
|
75
|
+
## Forms
|
|
76
|
+
|
|
77
|
+
### Controlled
|
|
24
78
|
|
|
25
|
-
|
|
79
|
+
Bind `value` and update state from `onCobInput`:
|
|
26
80
|
|
|
27
81
|
```tsx
|
|
28
|
-
|
|
29
|
-
|
|
82
|
+
const [name, setName] = React.useState("");
|
|
83
|
+
|
|
84
|
+
<CobTextinput
|
|
85
|
+
label="Name"
|
|
86
|
+
value={name}
|
|
87
|
+
onCobInput={(e) => setName(e.detail.value)}
|
|
88
|
+
/>;
|
|
89
|
+
```
|
|
30
90
|
|
|
31
|
-
|
|
91
|
+
The wrapper re-sets properties on every commit, so re-rendering with
|
|
92
|
+
the same value snaps the element back to your state.
|
|
93
|
+
|
|
94
|
+
### Uncontrolled with a native form
|
|
95
|
+
|
|
96
|
+
COBWDS form components are form-associated custom elements: they submit
|
|
97
|
+
with a plain `<form>` like native inputs.
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
function ContactForm() {
|
|
32
101
|
return (
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
102
|
+
<form
|
|
103
|
+
onSubmit={(e) => {
|
|
104
|
+
e.preventDefault();
|
|
105
|
+
const data = new FormData(e.currentTarget);
|
|
106
|
+
console.log(data.get("email"));
|
|
107
|
+
}}
|
|
108
|
+
>
|
|
109
|
+
<CobTextinput label="Email" name="email" type="email" />
|
|
110
|
+
<CobButton type="submit" label="Send" />
|
|
111
|
+
</form>
|
|
37
112
|
);
|
|
38
113
|
}
|
|
39
114
|
```
|
|
40
115
|
|
|
41
|
-
|
|
116
|
+
### React Hook Form
|
|
117
|
+
|
|
118
|
+
Use `Controller` and wire the field through the event detail:
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
<Controller
|
|
122
|
+
name="firstName"
|
|
123
|
+
control={control}
|
|
124
|
+
render={({ field }) => (
|
|
125
|
+
<CobTextinput
|
|
126
|
+
label="First name"
|
|
127
|
+
value={field.value}
|
|
128
|
+
onCobInput={(e) => field.onChange(e.detail.value)}
|
|
129
|
+
onCobBlur={field.onBlur}
|
|
130
|
+
/>
|
|
131
|
+
)}
|
|
132
|
+
/>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The `useCobField` helper collapses that to a prop bag. Pass the kind of
|
|
136
|
+
form control: `"value"` (default), `"checked"`, or `"files"`.
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
<Controller
|
|
140
|
+
name="subscribed"
|
|
141
|
+
control={control}
|
|
142
|
+
render={({ field }) => (
|
|
143
|
+
<CobCheckbox label="Subscribe" {...useCobField(field, "checked")} />
|
|
144
|
+
)}
|
|
145
|
+
/>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
To surface validation errors through the component's own error slot:
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
<CobTextinput
|
|
152
|
+
label="Name"
|
|
153
|
+
showError={!!errors.name}
|
|
154
|
+
errorMessage={errors.name?.message}
|
|
155
|
+
{...useCobField(field)}
|
|
156
|
+
/>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## SSR and Next.js
|
|
160
|
+
|
|
161
|
+
Every wrapper carries a `"use client"` directive, so the App Router
|
|
162
|
+
works with a normal import — no `"use client"` needed in your own files
|
|
163
|
+
unless they hold state:
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
// app/page.tsx (server component)
|
|
167
|
+
import { Signup } from "./signup"; // a client component that renders COBWDS wrappers
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
- **App Router:** import wrappers from any client component. A server
|
|
171
|
+
component can't render a wrapper directly; pass it through a client
|
|
172
|
+
boundary like the `Signup` example.
|
|
173
|
+
- **Pages Router:** import and use anywhere.
|
|
174
|
+
- Components render client side. There is no declarative shadow DOM
|
|
175
|
+
server rendering in this release.
|
|
176
|
+
|
|
177
|
+
## TypeScript tips
|
|
178
|
+
|
|
179
|
+
- `CobTextinputProps` is the full prop type:
|
|
180
|
+
`React.ComponentProps<typeof CobTextinput>`.
|
|
181
|
+
- `CobTextinputElement` is the element class type — use it for refs and
|
|
182
|
+
`e.target` casts.
|
|
183
|
+
- Event detail types come from the component packages:
|
|
184
|
+
`import type { CobTextinputInputEvent } from "@cobwds/cob-textinput"`.
|
|
185
|
+
- Unknown props are compile errors. If a prop is missing, check the
|
|
186
|
+
component's documented API — the wrapper exposes every public
|
|
187
|
+
property.
|
|
188
|
+
|
|
189
|
+
## Troubleshooting
|
|
190
|
+
|
|
191
|
+
**The element renders but has no styling or behavior.** The custom
|
|
192
|
+
element didn't upgrade. Import the component from `@cobwds/react` (the
|
|
193
|
+
import registers it); check the browser console for load errors. If you
|
|
194
|
+
render raw `<cob-*>` tags instead of wrappers, import the
|
|
195
|
+
`@cobwds/cob-*` package yourself.
|
|
196
|
+
|
|
197
|
+
**Everything renders unstyled.** Import `@cobwds/styles` once at the app
|
|
198
|
+
entry (see Load styles).
|
|
199
|
+
|
|
200
|
+
**Hooks error or "Invalid hook call".** Usually a duplicate React.
|
|
201
|
+
Check `npm ls react` and dedupe; `@cobwds/react` declares React as a
|
|
202
|
+
peer dependency, so a single copy must win.
|
|
203
|
+
|
|
204
|
+
**`onChange` never fires.** COBWDS components dispatch `cob-*` events.
|
|
205
|
+
Use `onCobChange` / `onCobInput`, not React's `onChange`.
|
|
206
|
+
|
|
207
|
+
**Rejecting user input from a handler.** If your handler sets state to
|
|
208
|
+
a value React considers unchanged, React skips the re-render and the
|
|
209
|
+
element keeps the user's text. Force a commit (state that always
|
|
210
|
+
changes) or write the property directly in the handler:
|
|
211
|
+
|
|
212
|
+
```tsx
|
|
213
|
+
onCobInput={(e) => {
|
|
214
|
+
const next = sanitize(e.detail.value);
|
|
215
|
+
setValue(next);
|
|
216
|
+
(e.target as CobTextinputElement).value = next;
|
|
217
|
+
}}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## How this package is built
|
|
221
|
+
|
|
222
|
+
`src/generated/` is written by the `@cobwds/codegen` CEM plugin on every
|
|
223
|
+
`npm run cem` and is not committed. Hand-written wrappers in
|
|
224
|
+
`src/overrides/` replace a generated file by class name. See
|
|
225
|
+
`src/overrides/README.md`.
|
|
42
226
|
|
|
43
|
-
|
|
44
|
-
- For event listeners, use `ref` and `addEventListener` — React's synthetic event system doesn't forward to custom elements
|
|
45
|
-
- Boolean attributes should be set explicitly: `<cob-button disabled={true} />`
|
|
227
|
+
## MCP guide additions
|
|
46
228
|
|
|
47
229
|
## Using the stepper in React (multi-page pattern)
|
|
48
230
|
|
|
@@ -53,7 +235,7 @@ function App() {
|
|
|
53
235
|
|
|
54
236
|
**Always pass both `current` and `selected` explicitly from React state.** Without explicit `selected`, the stepper's internal fallback will override the user's sidebar navigation on every React re-render.
|
|
55
237
|
|
|
56
|
-
**Event listening:** Use `
|
|
238
|
+
**Event listening:** Use `onCobStepClick` on each `CobStep` (not on `CobStepper`). Always call `e.preventDefault()` to suppress `href`-based page navigation.
|
|
57
239
|
|
|
58
240
|
**Do not use `href` for SPA routing.** Omit `href` and handle step changes entirely in React state.
|
|
59
241
|
|
|
@@ -61,7 +243,7 @@ function App() {
|
|
|
61
243
|
|
|
62
244
|
```tsx
|
|
63
245
|
import { useState } from 'react';
|
|
64
|
-
import { CobStepper, CobStep, CobButton } from '@cobwds/
|
|
246
|
+
import { CobStepper, CobStep, CobButton } from '@cobwds/react';
|
|
65
247
|
|
|
66
248
|
const STEPS = [
|
|
67
249
|
{ label: 'Personal Info', content: <PersonalInfoForm /> },
|
|
@@ -89,7 +271,7 @@ export function MultiStepForm() {
|
|
|
89
271
|
label={step.label}
|
|
90
272
|
current={i === currentStep}
|
|
91
273
|
selected={i === viewingStep}
|
|
92
|
-
|
|
274
|
+
onCobStepClick={(e) => { e.preventDefault(); setViewingStep(i); }}
|
|
93
275
|
/>
|
|
94
276
|
))}
|
|
95
277
|
<div slot="actions">
|
|
@@ -102,10 +284,10 @@ export function MultiStepForm() {
|
|
|
102
284
|
<div style={{ display: 'flex', gap: '8px', marginTop: '16px' }}>
|
|
103
285
|
{viewingStep > 0 && (
|
|
104
286
|
<CobButton label="Back" variant="outline"
|
|
105
|
-
|
|
287
|
+
onCobClick={() => setViewingStep((v) => v - 1)} />
|
|
106
288
|
)}
|
|
107
289
|
{viewingStep === currentStep && currentStep < STEPS.length - 1 && (
|
|
108
|
-
<CobButton label="Continue"
|
|
290
|
+
<CobButton label="Continue" onCobClick={handleContinue} />
|
|
109
291
|
)}
|
|
110
292
|
</div>
|
|
111
293
|
</main>
|