@alexandregme/zljs 1.10.0 → 1.12.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/CHANGELOG.md +60 -1
- package/dist/index.d.ts +33 -9
- package/dist/index.js +45 -10
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,63 @@ A change that needs action in an app has a **Migration** note. When updating, re
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [1.12.0] - 2026-11-02
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `TextArea` takes `aria-label`, `name`, `autoComplete`, `required`, `onBlur`, `onKeyDown` and `ref`, as `Input` and `Select` do, so it works with `register` from `react-hook-form`
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- `TextArea` requires `label` or `aria-label` in its type, instead of always `label`
|
|
20
|
+
- `TextArea` draws a real `focus-visible` outline in the theme's `primary` color instead of a `focus:ring`, so focus stays visible in forced colors mode, and an app's own color reaches it
|
|
21
|
+
- `TextArea` border is now `gray-500` instead of `gray-300`, for 3:1 contrast, and its placeholder is `gray-500`, for 4.5:1
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- `TextArea` with `error` sets `aria-invalid` and points `aria-describedby` to the message, so a screen reader announces both
|
|
26
|
+
- `TextArea` uses `resize-y`. It had a class that doesn't exist in Tailwind, so it only resized vertically in apps with Tailwind's base styles
|
|
27
|
+
|
|
28
|
+
#### Migration
|
|
29
|
+
|
|
30
|
+
Only code that extends `TextAreaProps` and tests that assert its old classes need a change. To find them, run the commands below from the app's root:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
grep -rnE "\bTextAreaProps\b" . --include='*.ts' --include='*.tsx' --exclude-dir=node_modules --exclude-dir=.next
|
|
34
|
+
grep -rnE "border-gray-300|focus:ring-blue-500|focus:border-transparent|resize-vertical" . --include='*.test.*' --exclude-dir=node_modules --exclude-dir=.next
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Replace `interface X extends TextAreaProps` with `type X = TextAreaProps & { ... }`, because `TextAreaProps` is now a union. In tests, replace those classes with `focus-visible:outline-zl-primary`, `border-gray-500` and `resize-y`.
|
|
38
|
+
|
|
39
|
+
## [1.11.0] - 2026-11-02
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
- `Select` takes `aria-label`, `name`, `autoComplete`, `required`, `onBlur` and `onKeyDown`, and forwards `ref`, as `Input` does, so it works with `register` from `react-hook-form`
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- `Select` requires `label` or `aria-label` in its type, instead of always `label`
|
|
48
|
+
- `Select` draws a real `focus-visible` outline in the theme's `primary` color instead of a `focus:ring`, so focus stays visible in forced colors mode, and an app's own color reaches it
|
|
49
|
+
- `Select` border is now `gray-500` instead of `gray-300`, for 3:1 contrast
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
|
|
53
|
+
- `Select` shows its `placeholder` when there is no `value`. It used to start on the first option, so the `placeholder` never showed
|
|
54
|
+
- `Select` with `error` sets `aria-invalid` and points `aria-describedby` to the message, so a screen reader announces both
|
|
55
|
+
|
|
56
|
+
#### Migration
|
|
57
|
+
|
|
58
|
+
Only a `Select` with a `placeholder` and no `value`, code that extends `SelectProps`, and tests that assert its old classes need a change. To find them, run the commands below from the app's root. The first also finds other components named `Select`, and those stay as they are:
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
grep -rnE -A6 "<Select([[:space:]/>]|$)" . --include='*.tsx' --exclude-dir=node_modules --exclude-dir=.next | grep -E "placeholder"
|
|
62
|
+
grep -rnE "\bSelectProps\b" . --include='*.ts' --include='*.tsx' --exclude-dir=node_modules --exclude-dir=.next
|
|
63
|
+
grep -rnE "border-gray-300|focus:ring-blue-500|focus:border-transparent" . --include='*.test.*' --exclude-dir=node_modules --exclude-dir=.next
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
That `Select` now starts empty, so `onChange` runs only after the user picks an option. If the app counted on the first option being selected, pass it as `value`. Replace `interface X extends SelectProps` with `type X = SelectProps & { ... }`, because `SelectProps` is now a union. In tests, replace those classes with `focus-visible:outline-zl-primary` and `border-gray-500`.
|
|
67
|
+
|
|
11
68
|
## [1.10.0] - 2026-11-02
|
|
12
69
|
|
|
13
70
|
### Added
|
|
@@ -77,7 +134,9 @@ Replace the shade with the class of the `color` being tested, for example `/(^|\
|
|
|
77
134
|
|
|
78
135
|
The message of `Error` is `text-zl-danger`. `none` and `ghost` didn't change.
|
|
79
136
|
|
|
80
|
-
[Unreleased]: https://github.com/alexandregme/zljs/compare/v1.
|
|
137
|
+
[Unreleased]: https://github.com/alexandregme/zljs/compare/v1.12.0...HEAD
|
|
138
|
+
[1.12.0]: https://github.com/alexandregme/zljs/compare/v1.11.0...v1.12.0
|
|
139
|
+
[1.11.0]: https://github.com/alexandregme/zljs/compare/v1.10.0...v1.11.0
|
|
81
140
|
[1.10.0]: https://github.com/alexandregme/zljs/compare/v1.9.0...v1.10.0
|
|
82
141
|
[1.9.0]: https://github.com/alexandregme/zljs/compare/v1.8.0...v1.9.0
|
|
83
142
|
[1.8.0]: https://github.com/alexandregme/zljs/compare/v1.7.0...v1.8.0
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { ReactNode, ChangeEvent, FormEvent, HTMLInputAutoCompleteAttribute, FocusEvent, KeyboardEvent } from 'react';
|
|
2
|
+
import { ReactNode, ChangeEvent, FormEvent, HTMLInputAutoCompleteAttribute, FocusEvent, KeyboardEvent, Ref } from 'react';
|
|
3
3
|
export { FormInput, FormInputProps, FormProvider, FormProviderProps } from './rhf/index.js';
|
|
4
4
|
export { toast } from 'sonner';
|
|
5
5
|
import * as zustand from 'zustand';
|
|
@@ -136,29 +136,53 @@ interface SelectOption {
|
|
|
136
136
|
value: string;
|
|
137
137
|
label: string;
|
|
138
138
|
}
|
|
139
|
-
|
|
139
|
+
type SelectName = {
|
|
140
140
|
label: string;
|
|
141
|
+
"aria-label"?: string;
|
|
142
|
+
} | {
|
|
143
|
+
label?: undefined;
|
|
144
|
+
"aria-label": string;
|
|
145
|
+
};
|
|
146
|
+
type SelectProps = SelectName & {
|
|
147
|
+
name?: string;
|
|
141
148
|
options: SelectOption[];
|
|
142
|
-
value?: string;
|
|
143
149
|
placeholder?: string;
|
|
144
|
-
|
|
150
|
+
autoComplete?: HTMLInputAutoCompleteAttribute;
|
|
151
|
+
value?: string;
|
|
145
152
|
error?: string;
|
|
153
|
+
disabled?: boolean;
|
|
154
|
+
required?: boolean;
|
|
146
155
|
onChange?: (event: ChangeEvent<HTMLSelectElement>) => void;
|
|
147
|
-
|
|
156
|
+
onBlur?: (event: FocusEvent<HTMLSelectElement>) => void;
|
|
157
|
+
onKeyDown?: (event: KeyboardEvent<HTMLSelectElement>) => void;
|
|
158
|
+
ref?: Ref<HTMLSelectElement>;
|
|
159
|
+
};
|
|
148
160
|
|
|
149
|
-
declare const Select: ({ label: labelText, options, value,
|
|
161
|
+
declare const Select: ({ label: labelText, "aria-label": ariaLabel, name, options, placeholder, autoComplete, value, error, disabled, required, onChange, onBlur, onKeyDown, ref, }: SelectProps) => react.JSX.Element;
|
|
150
162
|
|
|
151
|
-
|
|
163
|
+
type TextAreaName = {
|
|
152
164
|
label: string;
|
|
165
|
+
"aria-label"?: string;
|
|
166
|
+
} | {
|
|
167
|
+
label?: undefined;
|
|
168
|
+
"aria-label": string;
|
|
169
|
+
};
|
|
170
|
+
type TextAreaProps = TextAreaName & {
|
|
171
|
+
name?: string;
|
|
153
172
|
placeholder?: string;
|
|
173
|
+
autoComplete?: HTMLInputAutoCompleteAttribute;
|
|
154
174
|
value?: string;
|
|
155
175
|
rows?: number;
|
|
156
176
|
error?: string;
|
|
157
177
|
disabled?: boolean;
|
|
178
|
+
required?: boolean;
|
|
158
179
|
onChange?: (event: ChangeEvent<HTMLTextAreaElement>) => void;
|
|
159
|
-
|
|
180
|
+
onBlur?: (event: FocusEvent<HTMLTextAreaElement>) => void;
|
|
181
|
+
onKeyDown?: (event: KeyboardEvent<HTMLTextAreaElement>) => void;
|
|
182
|
+
ref?: Ref<HTMLTextAreaElement>;
|
|
183
|
+
};
|
|
160
184
|
|
|
161
|
-
declare const TextArea: ({ label: labelText, placeholder, value, rows, error, disabled, onChange, }: TextAreaProps) => react.JSX.Element;
|
|
185
|
+
declare const TextArea: ({ label: labelText, "aria-label": ariaLabel, name, placeholder, autoComplete, value, rows, error, disabled, required, onChange, onBlur, onKeyDown, ref, }: TextAreaProps) => react.JSX.Element;
|
|
162
186
|
|
|
163
187
|
declare const Icon: ({ name, size, "aria-label": ariaLabel, }: IconProps) => react.JSX.Element;
|
|
164
188
|
|
package/dist/index.js
CHANGED
|
@@ -356,30 +356,48 @@ var label3 = tv5({
|
|
|
356
356
|
base: "text-sm font-medium text-gray-700"
|
|
357
357
|
});
|
|
358
358
|
var select = tv5({
|
|
359
|
-
base: "px-3 py-2 border border-gray-
|
|
359
|
+
base: "px-3 py-2 border border-gray-500 rounded-md text-sm bg-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zl-primary disabled:bg-gray-100 disabled:cursor-not-allowed"
|
|
360
360
|
});
|
|
361
361
|
|
|
362
362
|
// core/forms/select/select.tsx
|
|
363
363
|
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
364
364
|
var Select = ({
|
|
365
365
|
label: labelText,
|
|
366
|
+
"aria-label": ariaLabel,
|
|
367
|
+
name,
|
|
366
368
|
options,
|
|
367
|
-
value,
|
|
368
369
|
placeholder,
|
|
369
|
-
|
|
370
|
+
autoComplete,
|
|
371
|
+
value,
|
|
370
372
|
error,
|
|
371
|
-
|
|
373
|
+
disabled,
|
|
374
|
+
required,
|
|
375
|
+
onChange,
|
|
376
|
+
onBlur,
|
|
377
|
+
onKeyDown,
|
|
378
|
+
ref
|
|
372
379
|
}) => {
|
|
373
380
|
const id = useId3();
|
|
381
|
+
const errorId = `${id}-error`;
|
|
374
382
|
return /* @__PURE__ */ jsxs4("div", { className: wrapper3(), children: [
|
|
375
|
-
/* @__PURE__ */ jsx6("label", { htmlFor: id, className: label3(), children: labelText }),
|
|
383
|
+
labelText && /* @__PURE__ */ jsx6("label", { htmlFor: id, className: label3(), children: labelText }),
|
|
376
384
|
/* @__PURE__ */ jsxs4(
|
|
377
385
|
"select",
|
|
378
386
|
{
|
|
387
|
+
ref,
|
|
379
388
|
id,
|
|
389
|
+
name,
|
|
390
|
+
autoComplete,
|
|
380
391
|
value,
|
|
392
|
+
defaultValue: placeholder && value === void 0 ? "" : void 0,
|
|
381
393
|
disabled,
|
|
394
|
+
required,
|
|
382
395
|
onChange,
|
|
396
|
+
onBlur,
|
|
397
|
+
onKeyDown,
|
|
398
|
+
"aria-label": ariaLabel,
|
|
399
|
+
"aria-invalid": error ? true : void 0,
|
|
400
|
+
"aria-describedby": error ? errorId : void 0,
|
|
383
401
|
className: select(),
|
|
384
402
|
children: [
|
|
385
403
|
placeholder && /* @__PURE__ */ jsx6("option", { value: "", disabled: true, children: placeholder }),
|
|
@@ -387,7 +405,7 @@ var Select = ({
|
|
|
387
405
|
]
|
|
388
406
|
}
|
|
389
407
|
),
|
|
390
|
-
error && /* @__PURE__ */ jsx6(Error, { message: error })
|
|
408
|
+
error && /* @__PURE__ */ jsx6(Error, { id: errorId, message: error })
|
|
391
409
|
] });
|
|
392
410
|
};
|
|
393
411
|
|
|
@@ -403,36 +421,53 @@ var label4 = tv6({
|
|
|
403
421
|
base: "text-sm font-medium text-gray-700"
|
|
404
422
|
});
|
|
405
423
|
var textarea = tv6({
|
|
406
|
-
base: "px-3 py-2 border border-gray-
|
|
424
|
+
base: "px-3 py-2 border border-gray-500 rounded-md text-sm placeholder:text-gray-500 resize-y focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zl-primary disabled:bg-gray-100 disabled:cursor-not-allowed"
|
|
407
425
|
});
|
|
408
426
|
|
|
409
427
|
// core/forms/text-area/text-area.tsx
|
|
410
428
|
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
411
429
|
var TextArea = ({
|
|
412
430
|
label: labelText,
|
|
431
|
+
"aria-label": ariaLabel,
|
|
432
|
+
name,
|
|
413
433
|
placeholder,
|
|
434
|
+
autoComplete,
|
|
414
435
|
value,
|
|
415
436
|
rows = 3,
|
|
416
437
|
error,
|
|
417
438
|
disabled,
|
|
418
|
-
|
|
439
|
+
required,
|
|
440
|
+
onChange,
|
|
441
|
+
onBlur,
|
|
442
|
+
onKeyDown,
|
|
443
|
+
ref
|
|
419
444
|
}) => {
|
|
420
445
|
const id = useId4();
|
|
446
|
+
const errorId = `${id}-error`;
|
|
421
447
|
return /* @__PURE__ */ jsxs5("div", { className: wrapper4(), children: [
|
|
422
|
-
/* @__PURE__ */ jsx7("label", { htmlFor: id, className: label4(), children: labelText }),
|
|
448
|
+
labelText && /* @__PURE__ */ jsx7("label", { htmlFor: id, className: label4(), children: labelText }),
|
|
423
449
|
/* @__PURE__ */ jsx7(
|
|
424
450
|
"textarea",
|
|
425
451
|
{
|
|
452
|
+
ref,
|
|
426
453
|
id,
|
|
454
|
+
name,
|
|
427
455
|
placeholder,
|
|
456
|
+
autoComplete,
|
|
428
457
|
value,
|
|
429
458
|
rows,
|
|
430
459
|
disabled,
|
|
460
|
+
required,
|
|
431
461
|
onChange,
|
|
462
|
+
onBlur,
|
|
463
|
+
onKeyDown,
|
|
464
|
+
"aria-label": ariaLabel,
|
|
465
|
+
"aria-invalid": error ? true : void 0,
|
|
466
|
+
"aria-describedby": error ? errorId : void 0,
|
|
432
467
|
className: textarea()
|
|
433
468
|
}
|
|
434
469
|
),
|
|
435
|
-
error && /* @__PURE__ */ jsx7(Error, { message: error })
|
|
470
|
+
error && /* @__PURE__ */ jsx7(Error, { id: errorId, message: error })
|
|
436
471
|
] });
|
|
437
472
|
};
|
|
438
473
|
|