@nysds/mcp-server 1.21.0 → 1.22.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 +6 -2
- package/data/cem/custom-elements.json +1791 -1119
- package/data/frameworks.json +14 -0
- package/data/guides/frameworks/angular.md +184 -26
- package/data/guides/frameworks/react-additions.md +69 -0
- package/data/guides/frameworks/react.md +205 -23
- package/data/guides/frameworks/vue.md +356 -0
- package/data/tokens/tokens.css +1 -1
- 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/package.json +6 -4
|
@@ -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 @nysds/mcp-server
|
|
5
|
+
-->
|
|
6
|
+
# @nysds/angular
|
|
2
7
|
|
|
3
|
-
|
|
8
|
+
Angular components for the New York State Design System (NYSDS). Generated from the NYSDS 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 @nysds/
|
|
22
|
+
npm install @nysds/angular @nysds/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 NYSDS styles to your `angular.json`:
|
|
14
30
|
|
|
15
31
|
```json
|
|
16
32
|
{
|
|
@@ -20,8 +36,7 @@ Add the NYSDS stylesheet to `angular.json`:
|
|
|
20
36
|
"build": {
|
|
21
37
|
"options": {
|
|
22
38
|
"styles": [
|
|
23
|
-
"node_modules/@nysds/styles/
|
|
24
|
-
"src/styles.css"
|
|
39
|
+
"node_modules/@nysds/styles/nysds-full.min.css"
|
|
25
40
|
]
|
|
26
41
|
}
|
|
27
42
|
}
|
|
@@ -31,41 +46,184 @@ Add the NYSDS 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 { NysButtonComponent } from '@nysds/angular';
|
|
57
|
+
@Component({
|
|
58
|
+
imports: [RouterOutlet, NysButtonComponent],
|
|
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
|
+
<nys-textinput
|
|
81
|
+
[label]="'Email'"
|
|
82
|
+
[required]="true"
|
|
83
|
+
(nysChange)="onEmailChange($event)"
|
|
84
|
+
></nys-textinput>
|
|
85
|
+
```
|
|
37
86
|
|
|
38
|
-
|
|
87
|
+
Event detail is typed:
|
|
39
88
|
|
|
40
89
|
```typescript
|
|
41
|
-
|
|
90
|
+
onEmailChange(event: NysTextinputChangeEvent) {
|
|
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
|
+
<nys-textinput [(ngModel)]="email" name="email"></nys-textinput>
|
|
111
|
+
<nys-checkbox [(ngModel)]="agreed" name="agreed"></nys-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
|
+
<nys-textinput formControlName="email"></nys-textinput>
|
|
129
|
+
<nys-checkbox formControlName="agreed"></nys-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
|
+
<nys-textinput [formField]="emailControl"></nys-textinput>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Group controls
|
|
147
|
+
|
|
148
|
+
`nys-checkboxgroup` and `nys-radiogroup` bind at group level:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
form = this.fb.group({
|
|
152
|
+
languages: [["en"], Validators.required],
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
```html
|
|
157
|
+
<nys-checkboxgroup formControlName="languages">
|
|
158
|
+
<nys-checkbox value="en">English</nys-checkbox>
|
|
159
|
+
<nys-checkbox value="es">Spanish</nys-checkbox>
|
|
160
|
+
</nys-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
|
+
<nys-textinput
|
|
171
|
+
formControlName="email"
|
|
172
|
+
nysControlErrors
|
|
173
|
+
></nys-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
|
+
<nys-textinput [formControl]="control"></nys-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
|
+
<nys-textinput></nys-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 { NysTextinputComponent } from "@nysds/angular/textinput";
|
|
224
|
+
import { NysCheckboxComponent } from "@nysds/angular/checkbox";
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## License
|
|
228
|
+
|
|
229
|
+
MIT
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
## Using the stepper in React (multi-page pattern)
|
|
2
|
+
|
|
3
|
+
`nys-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 `NysStep`.
|
|
6
|
+
- `viewingStep` — which step's content is currently displayed. Maps to the `selected` prop on `NysStep`.
|
|
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 `onNysStepClick` on each `NysStep` (not on `NysStepper`). 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 `<NysStepper>` inside a `<form>` element.**
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { useState } from 'react';
|
|
18
|
+
import { NysStepper, NysStep, NysButton } from '@nysds/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="nys-grid-row">
|
|
39
|
+
<NysStepper label="Application" className="nys-grid-col-12 nys-desktop:nys-grid-col-3">
|
|
40
|
+
{STEPS.map((step, i) => (
|
|
41
|
+
<NysStep
|
|
42
|
+
key={i}
|
|
43
|
+
label={step.label}
|
|
44
|
+
current={i === currentStep}
|
|
45
|
+
selected={i === viewingStep}
|
|
46
|
+
onNysStepClick={(e) => { e.preventDefault(); setViewingStep(i); }}
|
|
47
|
+
/>
|
|
48
|
+
))}
|
|
49
|
+
<div slot="actions">
|
|
50
|
+
<NysButton label="Save & Exit" variant="outline" fullWidth />
|
|
51
|
+
</div>
|
|
52
|
+
</NysStepper>
|
|
53
|
+
|
|
54
|
+
<main className="nys-grid-col-12 nys-desktop:nys-grid-col-9">
|
|
55
|
+
{STEPS[viewingStep].content}
|
|
56
|
+
<div style={{ display: 'flex', gap: '8px', marginTop: '16px' }}>
|
|
57
|
+
{viewingStep > 0 && (
|
|
58
|
+
<NysButton label="Back" variant="outline"
|
|
59
|
+
onNysClick={() => setViewingStep((v) => v - 1)} />
|
|
60
|
+
)}
|
|
61
|
+
{viewingStep === currentStep && currentStep < STEPS.length - 1 && (
|
|
62
|
+
<NysButton label="Continue" onNysClick={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 @nysds/mcp-server
|
|
5
|
+
-->
|
|
6
|
+
# @nysds/react
|
|
2
7
|
|
|
3
|
-
|
|
8
|
+
React components for the New York State Design System (NYSDS). Generated from the NYSDS 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 NYS Design System
|
|
21
|
+
```sh
|
|
22
|
+
npm install @nysds/react @nysds/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 "@nysds/styles/full";
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Without it, components render unstyled. `@nysds/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 NYSDS components
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import { NysButton } from "@nysds/react/button";
|
|
43
|
+
<NysButton 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 NYSDS names, mapped to `onNys*` props:
|
|
53
|
+
`nys-change` becomes `onNysChange`, `nys-input` becomes `onNysInput`.
|
|
54
|
+
The handler parameter is the typed event, so `e.detail` autocompletes:
|
|
14
55
|
|
|
15
56
|
```tsx
|
|
16
|
-
|
|
17
|
-
|
|
57
|
+
<NysTextinput
|
|
58
|
+
label="First name"
|
|
59
|
+
onNysInput={(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 NYSDS components don't dispatch — use
|
|
65
|
+
`onNysChange`.
|
|
66
|
+
|
|
67
|
+
**Refs** resolve to the element instance:
|
|
18
68
|
|
|
19
|
-
|
|
20
|
-
|
|
69
|
+
```tsx
|
|
70
|
+
const ref = React.useRef<NysTextinputElement>(null);
|
|
71
|
+
<NysTextinput 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 `onNysInput`:
|
|
26
80
|
|
|
27
81
|
```tsx
|
|
28
|
-
|
|
29
|
-
|
|
82
|
+
const [name, setName] = React.useState("");
|
|
83
|
+
|
|
84
|
+
<NysTextinput
|
|
85
|
+
label="Name"
|
|
86
|
+
value={name}
|
|
87
|
+
onNysInput={(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
|
+
NYSDS 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
|
+
<NysTextinput label="Email" name="email" type="email" />
|
|
110
|
+
<NysButton 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
|
+
<NysTextinput
|
|
126
|
+
label="First name"
|
|
127
|
+
value={field.value}
|
|
128
|
+
onNysInput={(e) => field.onChange(e.detail.value)}
|
|
129
|
+
onNysBlur={field.onBlur}
|
|
130
|
+
/>
|
|
131
|
+
)}
|
|
132
|
+
/>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The `useNysField` 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
|
+
<NysCheckbox label="Subscribe" {...useNysField(field, "checked")} />
|
|
144
|
+
)}
|
|
145
|
+
/>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
To surface validation errors through the component's own error slot:
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
<NysTextinput
|
|
152
|
+
label="Name"
|
|
153
|
+
showError={!!errors.name}
|
|
154
|
+
errorMessage={errors.name?.message}
|
|
155
|
+
{...useNysField(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 NYSDS 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
|
+
- `NysTextinputProps` is the full prop type:
|
|
180
|
+
`React.ComponentProps<typeof NysTextinput>`.
|
|
181
|
+
- `NysTextinputElement` 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 { NysTextinputInputEvent } from "@nysds/nys-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 `@nysds/react` (the
|
|
193
|
+
import registers it); check the browser console for load errors. If you
|
|
194
|
+
render raw `<nys-*>` tags instead of wrappers, import the
|
|
195
|
+
`@nysds/nys-*` package yourself.
|
|
196
|
+
|
|
197
|
+
**Everything renders unstyled.** Import `@nysds/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; `@nysds/react` declares React as a
|
|
202
|
+
peer dependency, so a single copy must win.
|
|
203
|
+
|
|
204
|
+
**`onChange` never fires.** NYSDS components dispatch `nys-*` events.
|
|
205
|
+
Use `onNysChange` / `onNysInput`, 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
|
+
onNysInput={(e) => {
|
|
214
|
+
const next = sanitize(e.detail.value);
|
|
215
|
+
setValue(next);
|
|
216
|
+
(e.target as NysTextinputElement).value = next;
|
|
217
|
+
}}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## How this package is built
|
|
221
|
+
|
|
222
|
+
`src/generated/` is written by the `@nysds/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: `<nys-button disabled={true} />`
|
|
227
|
+
## MCP guide additions
|
|
46
228
|
|
|
47
229
|
## Using the stepper in React (multi-page pattern)
|
|
48
230
|
|
|
@@ -61,7 +243,7 @@ function App() {
|
|
|
61
243
|
|
|
62
244
|
```tsx
|
|
63
245
|
import { useState } from 'react';
|
|
64
|
-
import { NysStepper, NysStep, NysButton } from '@nysds/
|
|
246
|
+
import { NysStepper, NysStep, NysButton } from '@nysds/react';
|
|
65
247
|
|
|
66
248
|
const STEPS = [
|
|
67
249
|
{ label: 'Personal Info', content: <PersonalInfoForm /> },
|