@edux-design/buttons 0.0.6 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edux-design/buttons",
3
- "version": "0.0.6",
3
+ "version": "0.1.1",
4
4
  "private": false,
5
5
  "sideEffects": [
6
6
  "**/*.css"
@@ -29,8 +29,10 @@
29
29
  "@tailwindcss/postcss": "^4.1.12"
30
30
  },
31
31
  "dependencies": {
32
- "react": "^19.1.0",
33
- "react-dom": "^19.1.0",
34
32
  "@edux-design/utils": "*"
33
+ },
34
+ "peerDependencies": {
35
+ "react": "^19.1.0",
36
+ "react-dom": "^19.1.0"
35
37
  }
36
38
  }
@@ -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
  });