@allxsmith/bestax-bulma 5.15.1 → 5.15.2

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.
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import { BulmaClassesProps } from '../helpers/useBulmaClasses';
3
+ import type { ConstrainedPolymorphicComponentWithoutRef } from '../helpers/polymorphic';
3
4
  /**
4
5
  * Checks if code is running in a browser environment.
5
6
  * @param win - Window object.
@@ -38,20 +39,48 @@ export interface DropdownProps extends Omit<React.HTMLAttributes<HTMLDivElement>
38
39
  id?: string;
39
40
  }
40
41
  /**
41
- * Props for the DropdownItem component.
42
+ * The elements a dropdown item may render as. Bulma's dropdown markup names
43
+ * these three and no others, so `as` is a closed set rather than an open
44
+ * `React.ElementType`.
42
45
  */
43
- export interface DropdownItemProps extends Omit<React.HTMLAttributes<HTMLElement>, keyof BulmaClassesProps>, BulmaClassesProps {
46
+ export type DropdownItemElement = 'a' | 'div' | 'button';
47
+ /**
48
+ * The DropdownItem component's own props.
49
+ */
50
+ export interface DropdownItemOwnProps extends BulmaClassesProps {
44
51
  /** Whether the item is active. */
45
52
  active?: boolean;
46
53
  /** Additional CSS classes. */
47
54
  className?: string;
48
- /** The element type to render. */
49
- as?: 'a' | 'div' | 'button';
50
55
  /** Marks the item as disabled; disabled items are skipped during keyboard navigation. Use with `as="button"` for a native disabled control, or pair with `aria-disabled` on a link. */
51
56
  disabled?: boolean;
52
57
  /** Item content. */
53
58
  children?: React.ReactNode;
54
59
  }
60
+ /**
61
+ * Props for the DropdownItem component. Everything the item does not own
62
+ * follows `as`: `href`, `target` and `rel` under the default `'a'`, `type` and
63
+ * `form` under `'button'`, and the shared HTML attributes under any of them.
64
+ *
65
+ * Pinning these to `React.HTMLAttributes<HTMLElement>` instead — which is what
66
+ * this type did before — rejected `href` on an anchor and `type` on a button,
67
+ * both of which have always worked at runtime. Same defect as #641, in the
68
+ * narrower shape a constrained `as` takes.
69
+ *
70
+ * **Name the tag you mean.** The type parameter defaults to the whole union, not
71
+ * to the rendered element, so bare `DropdownItemProps` keeps accepting
72
+ * `{ as: 'div' }` the way it always has. The cost is that it is not
73
+ * distributive: `Omit` over a union keeps only the shared keys, so the bare
74
+ * alias carries no `href` even though the component takes one under `as="a"`.
75
+ * Write `DropdownItemProps<'a'>` for the anchor's props. That is the #667 alias
76
+ * limitation Button carries, labelled next-major because closing it is
77
+ * source-breaking, and pinned for this component in
78
+ * `__typetests__/polymorphic.tsx`; the component itself is unaffected.
79
+ */
80
+ export type DropdownItemProps<T extends DropdownItemElement = DropdownItemElement> = DropdownItemOwnProps & Omit<React.ComponentPropsWithoutRef<T>, keyof DropdownItemOwnProps | 'as'> & {
81
+ /** The element type to render. */
82
+ as?: T;
83
+ };
55
84
  /**
56
85
  * Bulma Dropdown item.
57
86
  *
@@ -59,7 +88,7 @@ export interface DropdownItemProps extends Omit<React.HTMLAttributes<HTMLElement
59
88
  * @param {DropdownItemProps} props - Props for the DropdownItem component.
60
89
  * @returns {JSX.Element} The rendered dropdown item.
61
90
  */
62
- export declare const DropdownItem: React.FC<DropdownItemProps>;
91
+ export declare const DropdownItem: ConstrainedPolymorphicComponentWithoutRef<DropdownItemOwnProps, DropdownItemElement, "a">;
63
92
  /**
64
93
  * Bulma Dropdown divider.
65
94
  *
@@ -69,7 +98,7 @@ export declare const DropdownItem: React.FC<DropdownItemProps>;
69
98
  export declare const DropdownDivider: React.FC;
70
99
  /** Bulma Dropdown component with Item and Divider sub-components. */
71
100
  export declare const Dropdown: React.ForwardRefExoticComponent<DropdownProps & React.RefAttributes<HTMLDivElement>> & {
72
- Item: React.FC<DropdownItemProps>;
101
+ Item: ConstrainedPolymorphicComponentWithoutRef<DropdownItemOwnProps, DropdownItemElement, "a">;
73
102
  Divider: React.FC<{}>;
74
103
  };
75
104
  export default Dropdown;
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { BulmaClassesProps } from '../helpers/useBulmaClasses';
3
- import { IconChildrenProps, IconNameProps } from '../elements/Icon';
3
+ import { IconChildrenProps, IconDeprecatedProps, IconNameProps } from '../elements/Icon';
4
4
  /**
5
5
  * Props for the Panel component.
6
6
  */
@@ -45,7 +45,7 @@ export interface PanelBlockProps extends React.AnchorHTMLAttributes<HTMLAnchorEl
45
45
  * Props for the PanelIcon component.
46
46
  * Extends IconProps but uses 'panel-icon' as the container class.
47
47
  */
48
- export type PanelIconProps = Omit<IconNameProps, 'containerClassName'> | Omit<IconChildrenProps, 'containerClassName'>;
48
+ export type PanelIconProps = Omit<IconNameProps, 'containerClassName'> | Omit<IconChildrenProps, 'containerClassName'> | Omit<IconDeprecatedProps, 'containerClassName'>;
49
49
  /**
50
50
  * Props for the PanelInputBlock component.
51
51
  */
@@ -33,7 +33,14 @@ interface IconBaseProps extends React.HTMLAttributes<HTMLSpanElement>, BulmaClas
33
33
  color?: 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';
34
34
  /** Background color helper. */
35
35
  bgColor?: (typeof validColors)[number] | 'inherit' | 'current';
36
- /** DEPRECATED: Legacy prop, use `name` instead. */
36
+ /**
37
+ * Legacy icon class string (e.g. `'fas fa-star'`). Only its last segment is read, as the
38
+ * glyph name; the library still comes from `library` or `ConfigProvider`, so
39
+ * `'mdi mdi-rocket'` renders an `fa` class unless the effective library is already `mdi` —
40
+ * set it here or on the provider.
41
+ *
42
+ * @deprecated Use `name` instead.
43
+ */
37
44
  icon?: string;
38
45
  /**
39
46
  * The icon library to use ('fa' = Font Awesome, 'mdi' = Material Design Icons, 'ion' = Ionicons Web Components, 'material-icons' = Google Material Icons, 'material-symbols' = Google Material Symbols). Defaults to the value set in ConfigProvider or 'fa' if not configured. Ignored when `children` supplies the glyph instead of `name`.
@@ -44,7 +51,11 @@ interface IconBaseProps extends React.HTMLAttributes<HTMLSpanElement>, BulmaClas
44
51
  variant?: string;
45
52
  /** Additional modifiers (e.g. `'fa-lg'`, `'fa-spin'`, `'is-size-1'`). Ignored when `children` supplies the glyph instead of `name`. */
46
53
  features?: string | string[];
47
- /** **DEPRECATED:** Use `variant` and `features` instead. */
54
+ /**
55
+ * Additional modifiers in the older combined form, parsed into `variant` and `features`.
56
+ *
57
+ * @deprecated Use `variant` and `features` instead.
58
+ */
48
59
  libraryFeatures?: string | string[];
49
60
  /** Size modifier for the icon container. */
50
61
  size?: 'small' | 'medium' | 'large';
@@ -83,10 +94,38 @@ export interface IconChildrenProps extends IconBaseProps {
83
94
  children: Exclude<React.ReactNode, undefined>;
84
95
  }
85
96
  /**
86
- * Props for the Icon component — a discriminated union of a class-based `name` and a custom
87
- * `children` node.
97
+ * Props for `Icon` naming its glyph through the deprecated `icon` prop instead of `name`.
98
+ *
99
+ * The runtime has always accepted `icon` on its own — it strips a leading library prefix off
100
+ * the class string and falls through to the `name` path — but the type offered no member without
101
+ * a `name` or `children`, so every caller still on the deprecated prop got an error the
102
+ * package could not see — until #663 nothing here type-checked a test or a story, so the
103
+ * three `Icon.test.tsx` cases exercising this path proved the runtime and said nothing about
104
+ * the type. Declaring the path is the honest resolution: it is deprecated, not removed, and a
105
+ * deprecation that does not type-check is a removal announced only to whoever tries it.
106
+ *
107
+ * @deprecated Pass `name` instead. This member goes when `icon` does.
108
+ */
109
+ export interface IconDeprecatedProps extends IconBaseProps {
110
+ /**
111
+ * Legacy icon class string (e.g. `'fas fa-star'`). Only its last segment is read, as the
112
+ * glyph name; the library still comes from `library` or `ConfigProvider`, so
113
+ * `'mdi mdi-rocket'` renders an `fa` class unless the effective library is already `mdi` —
114
+ * set it here or on the provider.
115
+ *
116
+ * @deprecated Use `name` instead.
117
+ */
118
+ icon: string;
119
+ /** The icon name. Absent on this path — `icon` supplies the glyph. */
120
+ name?: undefined;
121
+ /** A custom node. Mutually exclusive with `icon`. */
122
+ children?: never;
123
+ }
124
+ /**
125
+ * Props for the Icon component — a discriminated union of a class-based `name`, a custom
126
+ * `children` node, and the deprecated `icon` class string.
88
127
  */
89
- export type IconProps = IconNameProps | IconChildrenProps;
128
+ export type IconProps = IconNameProps | IconChildrenProps | IconDeprecatedProps;
90
129
  /**
91
130
  * The `Icon` component is a Bulma-styled wrapper for displaying icons from various libraries (Font Awesome, Material Design Icons, Ionicons, Google Material Icons, Material Symbols, etc.).
92
131
  *
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import type { IconProps } from './Icon';
3
+ /**
4
+ * Distinguishes an `IconProps` object from a plain custom node (an inline SVG, a
5
+ * `react-icons` component, …) in a slot that accepts either — `Control`'s
6
+ * `iconLeft`/`iconRight`, `IconText`'s `iconProps`.
7
+ *
8
+ * One copy, imported by both consumers: two hand-maintained copies, one per
9
+ * consumer, is how the deprecated `icon` path came to crash both (#663).
10
+ *
11
+ * Its own module rather than `Icon.tsx` because `src/index.ts` wildcard-exports
12
+ * that file, and everything published there is public forever. `polymorphic.ts`
13
+ * makes the same call for `isCustomElement`: an internal discriminator is not
14
+ * something to support for the life of the package.
15
+ *
16
+ * It tests each member's VALUE, not merely its key, and only after everything
17
+ * React owns is out of the way. `'icon' in value` was enough
18
+ * to claim a Font Awesome `IconDefinition` — `{ prefix, iconName, icon: [w, h, …,
19
+ * path] }` — whose `icon` is a path array rather than a class string. That object
20
+ * is not a renderable node either, so the honest outcome is the one this slot has
21
+ * always had for it: the node branch, and React's own "Objects are not valid as a
22
+ * React child". Claiming it here instead rendered a blank container that still
23
+ * reserved layout.
24
+ *
25
+ * A member added to `IconProps` needs a line here, and a missed one is a crash
26
+ * rather than a fallthrough. That is the cost of a slot taking either a props
27
+ * object or a node; keeping the guard in one place is what keeps the cost to a
28
+ * single line.
29
+ */
30
+ export declare function isIconProps(value: IconProps | React.ReactNode): value is IconProps;
@@ -73,10 +73,14 @@ export type PolymorphicProps<T extends React.ElementType, Own> = T extends unkno
73
73
  * **A wrapping HOC erases the genericity.** `React.memo(Button)`,
74
74
  * `React.lazy`, a `styled()` wrapper — anything that infers its props through
75
75
  * `ComponentProps<T>` — instantiates the type parameter once and hands back a
76
- * component with a single widened prop type. Calls still work, but the checks
77
- * stop: `<MemoButton as="div" href="/x" />` compiles where `<Button>` rejects
78
- * it. This is inherent to polymorphic components in TypeScript, not something
79
- * this library can fix. Re-assert the type to get the checks back:
76
+ * component with a single prop type. It takes the LAST call signature, which is
77
+ * the derivation overload, so the wrapper is pinned to the DEFAULT element:
78
+ * `React.memo(Button)` rejects `as="div"` outright, and `React.memo(Dropdown.Item)`
79
+ * renders neither a `<div>` nor a `<button>`, though both components accept those
80
+ * directly. Stricter, not looser — the checks do not stop, they collapse onto one
81
+ * element, and a `Dropdown.Item` memoized for a long menu list is the case that
82
+ * meets it. This is inherent to polymorphic components in TypeScript, not
83
+ * something this library can fix. Re-assert the type to get the rest back:
80
84
  *
81
85
  * ```tsx
82
86
  * const MemoButton = React.memo(Button) as typeof Button;
@@ -103,3 +107,32 @@ export interface PolymorphicComponentWithoutRef<Own, Default extends React.Eleme
103
107
  (props: PolymorphicProps<Default, Own>): React.ReactElement | null;
104
108
  displayName?: string;
105
109
  }
110
+ /**
111
+ * A component whose props follow `as`, where `as` is a closed set of tags.
112
+ *
113
+ * `PolymorphicComponentWithoutRef` lets `as` name anything; this narrows it to
114
+ * `Allowed`. Bulma pins some elements by its own markup contract — a dropdown
115
+ * item is an `<a>`, a `<div>` or a `<button>` and nothing else — and the
116
+ * constraint keeps that promise while still deriving props from whichever of
117
+ * the three a caller picks.
118
+ *
119
+ * Genericity is what makes the derivation exact for a LITERAL `as`, and it is
120
+ * worth the extra type parameter. Typing such a component as `React.FC<Union>`
121
+ * instead — a union of the three prop shapes — reads as equivalent but is not:
122
+ * against a union target, an object literal's excess-property check passes if
123
+ * the property exists in ANY member, so `<Item as="div" href="/x" />` slips
124
+ * through. Inferring `T` from the literal checks against that one member.
125
+ *
126
+ * A UNION-typed `as` — a ternary, or a variable typed as the whole set — infers
127
+ * `T` as the union and lands back in that same permissive check, so
128
+ * `<Item as={tag} href="/x" />` compiles whatever `tag` turns out to be. That is
129
+ * the open `as` behaviour too, and deliberate there: `PolymorphicProps`
130
+ * distributes precisely so a union-typed `as` KEEPS each member's props rather
131
+ * than losing every prop the members do not share. Narrowing it is not a
132
+ * constrained-component question; it would have to change for `Button` first.
133
+ */
134
+ export interface ConstrainedPolymorphicComponentWithoutRef<Own, Allowed extends React.ElementType, Default extends Allowed> {
135
+ <T extends Allowed = Default>(props: PolymorphicProps<T, Own>): React.ReactElement | null;
136
+ (props: PolymorphicProps<Default, Own>): React.ReactElement | null;
137
+ displayName?: string;
138
+ }
@@ -100,7 +100,7 @@ export type { FormFieldProps } from './form/fieldProps';
100
100
  export * from './grid/Cell';
101
101
  export * from './grid/Grid';
102
102
  export * from './helpers/classNames';
103
- export type { PolymorphicComponent, PolymorphicComponentWithoutRef, PolymorphicProps, PolymorphicRef, } from './helpers/polymorphic';
103
+ export type { ConstrainedPolymorphicComponentWithoutRef, PolymorphicComponent, PolymorphicComponentWithoutRef, PolymorphicProps, PolymorphicRef, } from './helpers/polymorphic';
104
104
  export * from './helpers/mergeBulmaStyles';
105
105
  export * from './helpers/useBulmaClasses';
106
106
  export * from './helpers/Theme';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allxsmith/bestax-bulma",
3
- "version": "5.15.1",
3
+ "version": "5.15.2",
4
4
  "description": "A fully-typed React component library for the Bulma CSS framework. Build modern UIs quickly with reusable, accessible, and customizable Bulma-based React components.",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.esm.js",
@@ -190,6 +190,7 @@
190
190
  "bundle:stats": "rollup -c --configPlugin visualizer",
191
191
  "lint": "eslint src",
192
192
  "typecheck": "tsc --noEmit",
193
+ "typecheck:tests": "tsc -p tsconfig.test.json --noEmit",
193
194
  "format": "prettier --write \"src/**/*.{ts,tsx}\"",
194
195
  "format:check": "prettier --check \"src/**/*.{ts,tsx}\"",
195
196
  "clean": "rimraf dist",