@nysds/mcp-server 1.21.1 → 1.22.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.
@@ -0,0 +1,14 @@
1
+ {
2
+ "react": {
3
+ "packageName": "@nysds/react",
4
+ "version": "1.22.1"
5
+ },
6
+ "vue": {
7
+ "packageName": "@nysds/vue",
8
+ "version": "1.22.1"
9
+ },
10
+ "angular": {
11
+ "packageName": "@nysds/angular",
12
+ "version": "1.22.1"
13
+ }
14
+ }
@@ -1,16 +1,32 @@
1
- # NYSDS with Angular
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
- > **Note:** Angular integration is currently untested. The guidance below is based on standard web component usage patterns and may require adjustments.
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
- ## Installation
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/components @nysds/styles
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 the NYSDS stylesheet to `angular.json`:
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/dist/nysds-full.min.css",
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
- Use `nysds-full.min.css` for new projects (includes reset, typography, and utilities) or `nysds.min.css` for existing projects (CSS variables only).
49
+ ## First component
50
+
51
+ Import standalone components directly into your component:
35
52
 
36
- ## Configuration
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
- Add `CUSTOM_ELEMENTS_SCHEMA` to your module so Angular allows custom element tags:
87
+ Event detail is typed:
39
88
 
40
89
  ```typescript
41
- import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
90
+ onEmailChange(event: NysTextinputChangeEvent) {
91
+ console.log(event.detail.value); // autocompletes
92
+ }
93
+ ```
42
94
 
43
- @NgModule({
44
- schemas: [CUSTOM_ELEMENTS_SCHEMA]
45
- })
46
- export class AppModule {}
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
- For standalone components:
115
+ ### Reactive Forms
116
+
117
+ Use `formControl` or `formControlName`:
50
118
 
51
119
  ```typescript
52
- import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
120
+ form = this.fb.group({
121
+ email: ["", [Validators.required, Validators.email]],
122
+ agreed: [false, Validators.required],
123
+ });
124
+ ```
53
125
 
54
- @Component({
55
- schemas: [CUSTOM_ELEMENTS_SCHEMA],
56
- template: `<nys-button label="Click me"></nys-button>`
57
- })
58
- export class MyComponent {}
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
- ## Usage
176
+ The directive subscribes to `control.errors` and sets the component's `showError` and `errorMessage`. Override the default messages:
62
177
 
63
- Import components in the component or module where they're used:
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
- import '@nysds/nys-button';
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
- <nys-button label="Click me"></nys-button>
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
- # NYSDS with React
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
- > **Note:** React integration is currently untested. The guidance below is based on standard web component usage patterns and may require adjustments.
8
+ React components for the New York State Design System (NYSDS). Generated from the NYSDS web components library.
4
9
 
5
- ## Installation
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 install @nysds/components @nysds/styles
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 NYSDS stylesheet in your app entry point (e.g., `index.tsx` or `App.tsx`):
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
- // New project: includes reset, typography, and utility classes
17
- import '@nysds/styles/dist/nysds-full.min.css';
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
- // Existing project: CSS variables only (styles components, nothing else)
20
- // import '@nysds/styles/dist/nysds.min.css';
69
+ ```tsx
70
+ const ref = React.useRef<NysTextinputElement>(null);
71
+ <NysTextinput ref={ref} label="Name" />;
72
+ // ref.current?.checkValidity()
21
73
  ```
22
74
 
23
- ## Usage
75
+ ## Forms
76
+
77
+ ### Controlled
24
78
 
25
- Import components and use them in JSX:
79
+ Bind `value` and update state from `onNysInput`:
26
80
 
27
81
  ```tsx
28
- import '@nysds/nys-button';
29
- import '@nysds/nys-alert';
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
- function App() {
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
- <div>
34
- <nys-button label="Click me"></nys-button>
35
- <nys-alert type="info">This is an alert.</nys-alert>
36
- </div>
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
- ## Notes on web components in React
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
- - String attributes work normally: `<nys-button label="Click me" />`
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/components/react';
246
+ import { NysStepper, NysStep, NysButton } from '@nysds/react';
65
247
 
66
248
  const STEPS = [
67
249
  { label: 'Personal Info', content: <PersonalInfoForm /> },