@toteat-eng/ds-react 2026.8.25 → 2026.8.27
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/dist/components/Button/Button.d.ts +71 -3
- package/dist/components/Button/__stories__/Button.stories.d.ts +8 -0
- package/dist/index.es.js +642 -619
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +4 -4
- package/dist/index.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +5 -4
|
@@ -1,8 +1,43 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ComponentPropsWithoutRef, ElementType, ReactNode } from "react";
|
|
2
2
|
export type ButtonVariant = "primary" | "secondary" | "ghost" | "outline";
|
|
3
3
|
export type ButtonSize = "small" | "medium" | "large";
|
|
4
4
|
export type ButtonIconPosition = "left" | "right";
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The DS-owned half of the props — everything not inherited from the rendered element.
|
|
7
|
+
*
|
|
8
|
+
* **These names are RESERVED, and a collision fails SILENTLY. Read this before choosing an `as`
|
|
9
|
+
* target.** `ButtonProps` omits `keyof ButtonOwnProps` from the target's own props, and this
|
|
10
|
+
* component destructures each of them for its own use and never forwards them. So if the `as` target
|
|
11
|
+
* has a prop of its own called `size`, `variant`, `loading`, `icon`, `iconPosition`, `fullWidth`,
|
|
12
|
+
* `children` or `as`, that prop is stripped from the exposed type AND never reaches the target at
|
|
13
|
+
* runtime — with **no compile error**. Verified in review: an `as` target requiring `size: number`
|
|
14
|
+
* type-checks clean while receiving no `size` at all.
|
|
15
|
+
*
|
|
16
|
+
* This is inherent to the polymorphic-`as` pattern rather than a bug in this file, and it is harmless
|
|
17
|
+
* for the intended targets (`"a"`, a router `Link` — none of which own these names). But it is a
|
|
18
|
+
* foot-gun in a primitive three apps consume, so: if you need a target that owns one of these names,
|
|
19
|
+
* wrap it in a small adapter component that renames the prop, rather than reaching for `as` directly.
|
|
20
|
+
*/
|
|
21
|
+
interface ButtonOwnProps<C extends ElementType> {
|
|
22
|
+
/**
|
|
23
|
+
* Render as a different element or component instead of `<button>`.
|
|
24
|
+
*
|
|
25
|
+
* Exists for ONE reason: an action that NAVIGATES must be an anchor. A `<button>` that changes
|
|
26
|
+
* the route is a WCAG anti-pattern — it drops middle-click, open-in-new-tab, copy-link and the
|
|
27
|
+
* context menu, none of which a click handler can reinstate. Before this prop, a consumer wanting
|
|
28
|
+
* a link that looks like a button had to hand-copy this file's CSS, because the classes are
|
|
29
|
+
* CSS-module-hashed and unreachable from outside. That duplication then drifts on every DS release.
|
|
30
|
+
*
|
|
31
|
+
* This also honours rule 2 of the DS ("native element at the root — the most specific native
|
|
32
|
+
* element for the role") rather than working around it.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // TanStack Router
|
|
36
|
+
* <Button as={Link} variant="ghost" to="/orders/$id" params={{ id }}>Ver orden</Button>
|
|
37
|
+
* // plain anchor
|
|
38
|
+
* <Button as="a" href="https://toteat.com">Sitio</Button>
|
|
39
|
+
*/
|
|
40
|
+
as?: C;
|
|
6
41
|
variant?: ButtonVariant;
|
|
7
42
|
size?: ButtonSize;
|
|
8
43
|
loading?: boolean;
|
|
@@ -10,5 +45,38 @@ export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
|
10
45
|
icon?: ReactNode;
|
|
11
46
|
iconPosition?: ButtonIconPosition;
|
|
12
47
|
children: ReactNode;
|
|
48
|
+
/**
|
|
49
|
+
* Promoted to a DS-owned prop (was inherited from `ButtonHTMLAttributes`).
|
|
50
|
+
*
|
|
51
|
+
* It has to live here, not on the target: `disabled` is not a valid anchor attribute, so once `as`
|
|
52
|
+
* exists an inherited `disabled` type-checks ONLY for the `<button>` root. That made the entire
|
|
53
|
+
* disabled-anchor path — `aria-disabled`, `tabIndex={-1}`, click suppression, all implemented and
|
|
54
|
+
* tested — unreachable from typed consumer code with a `TS2322`. Caught by writing the
|
|
55
|
+
* `AsAnchor` story; the browser tests missed it because test files sit outside
|
|
56
|
+
* `tsconfig.app.json`.
|
|
57
|
+
*
|
|
58
|
+
* Declaring it here means "disabled" is one DS concept whatever the root element, and each root
|
|
59
|
+
* expresses it natively: `disabled` on a `<button>`, `aria-disabled` + `tabIndex={-1}` elsewhere.
|
|
60
|
+
*/
|
|
61
|
+
disabled?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Declared here for the same reason as `disabled` above: this component destructures `type` and
|
|
64
|
+
* consumes it, so leaving it to be inherited from the rendered element's attribute surface made
|
|
65
|
+
* the public type disagree with the runtime. A target that owns a `type` prop would have
|
|
66
|
+
* type-checked and then silently never received it.
|
|
67
|
+
*
|
|
68
|
+
* Applies to the `<button>` root ONLY — it is the native form-submit guard, and `type` means
|
|
69
|
+
* something unrelated on an `<a>`. Passing it alongside `as` is accepted by the type but has no
|
|
70
|
+
* effect, which is the same trade the reserved-name caveat above describes. Raised in review on
|
|
71
|
+
* https://github.com/toteat/ds-react/pull/57.
|
|
72
|
+
*/
|
|
73
|
+
type?: "button" | "submit" | "reset";
|
|
13
74
|
}
|
|
14
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Props accept whatever the rendered element accepts, so `as={Link}` type-checks `to`/`params` and
|
|
77
|
+
* `as="a"` type-checks `href`. Defaults to `"button"`, so the pre-existing
|
|
78
|
+
* `ButtonProps` (no type argument) keeps meaning exactly what it meant before — this is additive.
|
|
79
|
+
*/
|
|
80
|
+
export type ButtonProps<C extends ElementType = "button"> = ButtonOwnProps<C> & Omit<ComponentPropsWithoutRef<C>, keyof ButtonOwnProps<C>>;
|
|
81
|
+
export declare function Button<C extends ElementType = "button">({ as, variant, size, loading, fullWidth, icon, iconPosition, disabled, children, className, type, ...props }: ButtonProps<C>): import("react/jsx-runtime").JSX.Element;
|
|
82
|
+
export {};
|
|
@@ -14,3 +14,11 @@ export declare const Small: Story;
|
|
|
14
14
|
export declare const Large: Story;
|
|
15
15
|
export declare const AllVariants: Story;
|
|
16
16
|
export declare const AllSizes: Story;
|
|
17
|
+
/**
|
|
18
|
+
* `as` renders a real anchor while keeping the DS styling, for an action that NAVIGATES. Visual
|
|
19
|
+
* states worth eyeballing here rather than in a browser test: the focus ring on an `<a>` (it is the
|
|
20
|
+
* DS ring, not the UA default, because the hashed class lands on the anchor), and how a disabled
|
|
21
|
+
* anchor looks — it is styled via `aria-disabled`, not the `:disabled` pseudo-class the `<button>`
|
|
22
|
+
* path uses, so the two disabled looks can drift apart without anything failing.
|
|
23
|
+
*/
|
|
24
|
+
export declare const AsAnchor: Story;
|