@edux-design/buttons 0.1.0 → 0.1.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @edux-design/buttons
2
2
 
3
+ ## 0.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Minor token updates
8
+
3
9
  ## 0.1.0
4
10
 
5
11
  ### Minor Changes
package/dist/index.d.mts CHANGED
@@ -60,6 +60,8 @@ type IconButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
60
60
  *
61
61
  * @typedef {React.ButtonHTMLAttributes<HTMLButtonElement> & {
62
62
  * children?: React.ReactNode,
63
+ * startIcon?: React.ReactNode,
64
+ * endIcon?: React.ReactNode,
63
65
  * size?: "small"|"medium"|"large",
64
66
  * isSecondary?: boolean,
65
67
  * isDanger?: boolean,
@@ -94,6 +96,8 @@ declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAtt
94
96
  */
95
97
  type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
96
98
  children?: React.ReactNode;
99
+ startIcon?: React.ReactNode;
100
+ endIcon?: React.ReactNode;
97
101
  size?: "small" | "medium" | "large";
98
102
  isSecondary?: boolean;
99
103
  isDanger?: boolean;
package/dist/index.d.ts CHANGED
@@ -60,6 +60,8 @@ type IconButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
60
60
  *
61
61
  * @typedef {React.ButtonHTMLAttributes<HTMLButtonElement> & {
62
62
  * children?: React.ReactNode,
63
+ * startIcon?: React.ReactNode,
64
+ * endIcon?: React.ReactNode,
63
65
  * size?: "small"|"medium"|"large",
64
66
  * isSecondary?: boolean,
65
67
  * isDanger?: boolean,
@@ -94,6 +96,8 @@ declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAtt
94
96
  */
95
97
  type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
96
98
  children?: React.ReactNode;
99
+ startIcon?: React.ReactNode;
100
+ endIcon?: React.ReactNode;
97
101
  size?: "small" | "medium" | "large";
98
102
  isSecondary?: boolean;
99
103
  isDanger?: boolean;
package/dist/index.js CHANGED
@@ -102,6 +102,8 @@ var import_jsx_runtime2 = require("react/jsx-runtime");
102
102
  var Button = (0, import_react2.forwardRef)(
103
103
  ({
104
104
  children,
105
+ startIcon,
106
+ endIcon,
105
107
  size = "medium",
106
108
  isSecondary,
107
109
  isDanger,
@@ -112,19 +114,27 @@ var Button = (0, import_react2.forwardRef)(
112
114
  ...props
113
115
  }, ref) => {
114
116
  let variant = "primary";
115
- if (isBare) variant = "bare";
116
- if (isSecondary) variant = "secondary";
117
- if (isDanger) variant = "danger";
118
- if (disabled) variant = "disabled";
117
+ if (disabled) {
118
+ variant = "disabled";
119
+ } else if (isDanger && isBare) {
120
+ variant = "dangerBare";
121
+ } else if (isDanger) {
122
+ variant = "danger";
123
+ } else if (isSecondary) {
124
+ variant = "secondary";
125
+ } else if (isBare) {
126
+ variant = "bare";
127
+ }
119
128
  if (process.env.NODE_ENV !== "production") {
120
129
  const count = [isSecondary, isDanger, isBare].filter(Boolean).length;
121
- if (count > 1) {
130
+ const hasAllowedCombo = isDanger && isBare && count === 2;
131
+ if (count > 1 && !hasAllowedCombo) {
122
132
  console.warn(
123
133
  "[Button]: Multiple variant props passed. Using the highest priority only."
124
134
  );
125
135
  }
126
136
  }
127
- const defaults = "flex justify-center items-center focus:shadow-focus focus-visible:shadow-focus outline-none transition-all duration-300 ease-in-out cursor-pointer";
137
+ const defaults = "flex justify-center items-center gap-8 focus:shadow-focus focus-visible:shadow-focus outline-none transition-all duration-300 ease-in-out cursor-pointer";
128
138
  const fonts = "text-bold tracking-normal hover:underline hover:underline-offset-(--underline-offset-btn) active:underline active:underline-offset-(--underline-offset-btn)";
129
139
  const sizes = {
130
140
  small: "h-[36px] py-8 px-16 rounded-lg text-sm",
@@ -136,11 +146,12 @@ var Button = (0, import_react2.forwardRef)(
136
146
  primary: "border-2 border-border-primary-emphasis bg-bg-primary-base text-white hover:bg-border-focus hover:text-fg-base hover:border-border-emphasis hover:shadow-md active:bg-border-focus active:bg-bg-focus-overlay active:text-fg-base",
137
147
  secondary: "border-2 border-border-primary-base bg-bg-base text-fg-primary-emphasis hover:shadow-md hover:bg-bg-primary-subtle active:bg-bg-primary-subtle-90",
138
148
  bare: "bg-bg-base text-fg-primary-emphasis hover:bg-bg-minimal active:bg-bg-minimal-5",
149
+ dangerBare: "bg-bg-base text-fg-danger-emphasis hover:bg-bg-danger-subtle active:bg-bg-danger-subtle",
139
150
  danger: "bg-bg-danger-base border-2 border-border-danger-emphasis text-white hover:bg-bg-danger-base-5 active:bg-bg-danger-base-10",
140
151
  disabled: "border-2 border-border-subtle bg-bg-inactive text-fg-inactive hover:no-underline"
141
152
  };
142
153
  const STYLES = (0, import_utils2.cx)(defaults, sizes[size], width, fonts, variants[variant]);
143
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
154
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
144
155
  "button",
145
156
  {
146
157
  role: "button",
@@ -150,7 +161,11 @@ var Button = (0, import_react2.forwardRef)(
150
161
  className: STYLES,
151
162
  ref,
152
163
  ...props,
153
- children
164
+ children: [
165
+ startIcon,
166
+ children ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children }) : null,
167
+ endIcon
168
+ ]
154
169
  }
155
170
  );
156
171
  }
package/dist/index.mjs CHANGED
@@ -59,10 +59,12 @@ var IconButton = forwardRef(
59
59
  // src/elements/Button.jsx
60
60
  import React2, { forwardRef as forwardRef2 } from "react";
61
61
  import { cx as cx2 } from "@edux-design/utils";
62
- import { jsx as jsx2 } from "react/jsx-runtime";
62
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
63
63
  var Button = forwardRef2(
64
64
  ({
65
65
  children,
66
+ startIcon,
67
+ endIcon,
66
68
  size = "medium",
67
69
  isSecondary,
68
70
  isDanger,
@@ -73,19 +75,27 @@ var Button = forwardRef2(
73
75
  ...props
74
76
  }, ref) => {
75
77
  let variant = "primary";
76
- if (isBare) variant = "bare";
77
- if (isSecondary) variant = "secondary";
78
- if (isDanger) variant = "danger";
79
- if (disabled) variant = "disabled";
78
+ if (disabled) {
79
+ variant = "disabled";
80
+ } else if (isDanger && isBare) {
81
+ variant = "dangerBare";
82
+ } else if (isDanger) {
83
+ variant = "danger";
84
+ } else if (isSecondary) {
85
+ variant = "secondary";
86
+ } else if (isBare) {
87
+ variant = "bare";
88
+ }
80
89
  if (process.env.NODE_ENV !== "production") {
81
90
  const count = [isSecondary, isDanger, isBare].filter(Boolean).length;
82
- if (count > 1) {
91
+ const hasAllowedCombo = isDanger && isBare && count === 2;
92
+ if (count > 1 && !hasAllowedCombo) {
83
93
  console.warn(
84
94
  "[Button]: Multiple variant props passed. Using the highest priority only."
85
95
  );
86
96
  }
87
97
  }
88
- const defaults = "flex justify-center items-center focus:shadow-focus focus-visible:shadow-focus outline-none transition-all duration-300 ease-in-out cursor-pointer";
98
+ const defaults = "flex justify-center items-center gap-8 focus:shadow-focus focus-visible:shadow-focus outline-none transition-all duration-300 ease-in-out cursor-pointer";
89
99
  const fonts = "text-bold tracking-normal hover:underline hover:underline-offset-(--underline-offset-btn) active:underline active:underline-offset-(--underline-offset-btn)";
90
100
  const sizes = {
91
101
  small: "h-[36px] py-8 px-16 rounded-lg text-sm",
@@ -97,11 +107,12 @@ var Button = forwardRef2(
97
107
  primary: "border-2 border-border-primary-emphasis bg-bg-primary-base text-white hover:bg-border-focus hover:text-fg-base hover:border-border-emphasis hover:shadow-md active:bg-border-focus active:bg-bg-focus-overlay active:text-fg-base",
98
108
  secondary: "border-2 border-border-primary-base bg-bg-base text-fg-primary-emphasis hover:shadow-md hover:bg-bg-primary-subtle active:bg-bg-primary-subtle-90",
99
109
  bare: "bg-bg-base text-fg-primary-emphasis hover:bg-bg-minimal active:bg-bg-minimal-5",
110
+ dangerBare: "bg-bg-base text-fg-danger-emphasis hover:bg-bg-danger-subtle active:bg-bg-danger-subtle",
100
111
  danger: "bg-bg-danger-base border-2 border-border-danger-emphasis text-white hover:bg-bg-danger-base-5 active:bg-bg-danger-base-10",
101
112
  disabled: "border-2 border-border-subtle bg-bg-inactive text-fg-inactive hover:no-underline"
102
113
  };
103
114
  const STYLES = cx2(defaults, sizes[size], width, fonts, variants[variant]);
104
- return /* @__PURE__ */ jsx2(
115
+ return /* @__PURE__ */ jsxs(
105
116
  "button",
106
117
  {
107
118
  role: "button",
@@ -111,7 +122,11 @@ var Button = forwardRef2(
111
122
  className: STYLES,
112
123
  ref,
113
124
  ...props,
114
- children
125
+ children: [
126
+ startIcon,
127
+ children ? /* @__PURE__ */ jsx2("span", { children }) : null,
128
+ endIcon
129
+ ]
115
130
  }
116
131
  );
117
132
  }
@@ -128,7 +143,7 @@ var MenuButtonContext = createContext();
128
143
  import { cx as cx3 } from "@edux-design/utils";
129
144
 
130
145
  // ../icons/dist/index.mjs
131
- import { jsxs, jsx as jsx3 } from "react/jsx-runtime";
146
+ import { jsxs as jsxs2, jsx as jsx3 } from "react/jsx-runtime";
132
147
  var SvgChevron = (props) => /* @__PURE__ */ jsx3(
133
148
  "svg",
134
149
  {
@@ -152,7 +167,7 @@ var SvgChevron = (props) => /* @__PURE__ */ jsx3(
152
167
  var Chevron_default = SvgChevron;
153
168
 
154
169
  // src/elements/MenuButton.jsx
155
- import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
170
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
156
171
  function MenuButton({ children, size }) {
157
172
  const SPLIT_BUTTON_STYLES = cx3(
158
173
  "w-[36px] border-l-[1px] border-l-black flex flex-row justify-center items-center",
@@ -161,7 +176,7 @@ function MenuButton({ children, size }) {
161
176
  "h-[40px] py-2": size === "medium" || size === void 0
162
177
  }
163
178
  );
164
- return /* @__PURE__ */ jsxs2(MenuButtonContext.Provider, { value: true, children: [
179
+ return /* @__PURE__ */ jsxs3(MenuButtonContext.Provider, { value: true, children: [
165
180
  /* @__PURE__ */ jsx4("div", { className: SPLIT_BUTTON_STYLES, children }),
166
181
  /* @__PURE__ */ jsx4(Chevron_default, {})
167
182
  ] });
@@ -178,7 +193,7 @@ import React5, {
178
193
  useState
179
194
  } from "react";
180
195
  import { cx as cx4, filterChildrenByDisplayName } from "@edux-design/utils";
181
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
196
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
182
197
  var SIZE_STYLES = {
183
198
  small: {
184
199
  button: "text-xs px-12 py-6 rounded-md "
@@ -371,7 +386,7 @@ var ButtonGroup = forwardRef3(function ButtonGroup2({
371
386
  ref: setContainerRef,
372
387
  role: "radiogroup",
373
388
  className: rootClasses,
374
- children: /* @__PURE__ */ jsxs3("div", { className: trackClasses, children: [
389
+ children: /* @__PURE__ */ jsxs4("div", { className: trackClasses, children: [
375
390
  /* @__PURE__ */ jsx5(
376
391
  "span",
377
392
  {
@@ -434,7 +449,7 @@ var ButtonGroup = forwardRef3(function ButtonGroup2({
434
449
  const hasStartAdornment = option.startAdornment !== void 0 && option.startAdornment !== null;
435
450
  const endAdornment = option.endAdornment ?? option.endIcon;
436
451
  const hasEndAdornment = endAdornment !== void 0 && endAdornment !== null;
437
- return /* @__PURE__ */ jsxs3(
452
+ return /* @__PURE__ */ jsxs4(
438
453
  "button",
439
454
  {
440
455
  ref: registerButtonRef(option.value),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edux-design/buttons",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "private": false,
5
5
  "sideEffects": [
6
6
  "**/*.css"
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
  import { Button, IconButton } from "./../index";
3
- import { Close } from "@edux-design/icons";
3
+ import { Close, External, Warning } from "@edux-design/icons";
4
4
 
5
5
  export default {
6
6
  title: "Components/Button",
@@ -15,6 +15,8 @@ export default {
15
15
  isBare: { control: "boolean" },
16
16
  isStretched: { control: "boolean" },
17
17
  disabled: { control: "boolean" },
18
+ startIcon: { control: false },
19
+ endIcon: { control: false },
18
20
  children: { control: "text" },
19
21
  },
20
22
  };
@@ -48,6 +50,13 @@ Bare.args = {
48
50
  isBare: true,
49
51
  };
50
52
 
53
+ export const DangerBare = Template.bind({});
54
+ DangerBare.args = {
55
+ children: "Log out",
56
+ isBare: true,
57
+ isDanger: true,
58
+ };
59
+
51
60
  export const Disabled = Template.bind({});
52
61
  Disabled.args = {
53
62
  children: "Disabled",
@@ -82,3 +91,13 @@ export const Default = () => (
82
91
  <IconButton icon={<Close />} disabled aria-label="Disabled" />
83
92
  </div>
84
93
  );
94
+
95
+ export const WithIcons = () => (
96
+ <div className="flex gap-4 items-center">
97
+ <Button startIcon={<Warning />}>Start icon</Button>
98
+ <Button endIcon={<External />}>End icon</Button>
99
+ <Button isDanger isBare endIcon={<External />}>
100
+ Log out
101
+ </Button>
102
+ </div>
103
+ );
@@ -9,6 +9,8 @@ import { cx } from "@edux-design/utils";
9
9
  *
10
10
  * @typedef {React.ButtonHTMLAttributes<HTMLButtonElement> & {
11
11
  * children?: React.ReactNode,
12
+ * startIcon?: React.ReactNode,
13
+ * endIcon?: React.ReactNode,
12
14
  * size?: "small"|"medium"|"large",
13
15
  * isSecondary?: boolean,
14
16
  * isDanger?: boolean,
@@ -39,6 +41,8 @@ export const Button = forwardRef(
39
41
  (
40
42
  {
41
43
  children,
44
+ startIcon,
45
+ endIcon,
42
46
  size = "medium",
43
47
  isSecondary,
44
48
  isDanger,
@@ -51,15 +55,23 @@ export const Button = forwardRef(
51
55
  ref
52
56
  ) => {
53
57
  let variant = "primary";
54
- if (isBare) variant = "bare";
55
- if (isSecondary) variant = "secondary";
56
- if (isDanger) variant = "danger";
57
- if (disabled) variant = "disabled";
58
+ if (disabled) {
59
+ variant = "disabled";
60
+ } else if (isDanger && isBare) {
61
+ variant = "dangerBare";
62
+ } else if (isDanger) {
63
+ variant = "danger";
64
+ } else if (isSecondary) {
65
+ variant = "secondary";
66
+ } else if (isBare) {
67
+ variant = "bare";
68
+ }
58
69
 
59
70
  // handle conflicting props - the last bool wins
60
71
  if (process.env.NODE_ENV !== "production") {
61
72
  const count = [isSecondary, isDanger, isBare].filter(Boolean).length;
62
- if (count > 1) {
73
+ const hasAllowedCombo = isDanger && isBare && count === 2;
74
+ if (count > 1 && !hasAllowedCombo) {
63
75
  console.warn(
64
76
  "[Button]: Multiple variant props passed. Using the highest priority only."
65
77
  );
@@ -67,7 +79,7 @@ export const Button = forwardRef(
67
79
  }
68
80
 
69
81
  const defaults =
70
- "flex justify-center items-center focus:shadow-focus focus-visible:shadow-focus outline-none transition-all duration-300 ease-in-out cursor-pointer";
82
+ "flex justify-center items-center gap-8 focus:shadow-focus focus-visible:shadow-focus outline-none transition-all duration-300 ease-in-out cursor-pointer";
71
83
 
72
84
  const fonts =
73
85
  "text-bold tracking-normal hover:underline hover:underline-offset-(--underline-offset-btn) active:underline active:underline-offset-(--underline-offset-btn)";
@@ -86,6 +98,8 @@ export const Button = forwardRef(
86
98
  secondary:
87
99
  "border-2 border-border-primary-base bg-bg-base text-fg-primary-emphasis hover:shadow-md hover:bg-bg-primary-subtle active:bg-bg-primary-subtle-90",
88
100
  bare: "bg-bg-base text-fg-primary-emphasis hover:bg-bg-minimal active:bg-bg-minimal-5",
101
+ dangerBare:
102
+ "bg-bg-base text-fg-danger-emphasis hover:bg-bg-danger-subtle active:bg-bg-danger-subtle",
89
103
  danger:
90
104
  "bg-bg-danger-base border-2 border-border-danger-emphasis text-white hover:bg-bg-danger-base-5 active:bg-bg-danger-base-10",
91
105
  disabled:
@@ -104,7 +118,9 @@ export const Button = forwardRef(
104
118
  ref={ref}
105
119
  {...props}
106
120
  >
107
- {children}
121
+ {startIcon}
122
+ {children ? <span>{children}</span> : null}
123
+ {endIcon}
108
124
  </button>
109
125
  );
110
126
  }
@@ -52,6 +52,18 @@ describe("Button", () => {
52
52
  expect(screen.getByRole("button").className).toMatch(/bg-bg-danger-base/);
53
53
  });
54
54
 
55
+ it("applies danger bare variant when isDanger and isBare", () => {
56
+ render(
57
+ <Button isDanger isBare>
58
+ Danger Bare
59
+ </Button>
60
+ );
61
+ const btn = screen.getByRole("button");
62
+ expect(btn.className).toMatch(/text-fg-danger-emphasis/);
63
+ expect(btn.className).toMatch(/bg-bg-base/);
64
+ expect(btn.className).not.toMatch(/bg-bg-danger-base/);
65
+ });
66
+
55
67
  it("applies disabled variant when disabled=true", () => {
56
68
  render(<Button disabled>Disabled</Button>);
57
69
  const btn = screen.getByRole("button");
@@ -76,4 +88,33 @@ describe("Button", () => {
76
88
 
77
89
  warnSpy.mockRestore();
78
90
  });
91
+
92
+ it("renders start and end icons when provided", () => {
93
+ render(
94
+ <Button
95
+ startIcon={<span data-testid="start-icon">S</span>}
96
+ endIcon={<span data-testid="end-icon">E</span>}
97
+ >
98
+ With icons
99
+ </Button>
100
+ );
101
+
102
+ expect(screen.getByTestId("start-icon")).toBeInTheDocument();
103
+ expect(screen.getByTestId("end-icon")).toBeInTheDocument();
104
+ });
105
+
106
+ it("does not warn for the danger bare combination", () => {
107
+ const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
108
+ process.env.NODE_ENV = "development";
109
+
110
+ render(
111
+ <Button isDanger isBare>
112
+ Allowed
113
+ </Button>
114
+ );
115
+
116
+ expect(warnSpy).not.toHaveBeenCalled();
117
+
118
+ warnSpy.mockRestore();
119
+ });
79
120
  });