Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/mighty-towns-worry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cloudoperators/juno-ui-components": minor
---

Add extra-small (`xs`) size to Button component. Spinner margin adjustment will be addressed in a separate PR.
78 changes: 60 additions & 18 deletions packages/ui-components/src/components/Button/Button.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ const btnSmallBase = `
jn:leading-5
`

const btnXSBase = `
jn:text-xs
Comment thread
MartinS-git marked this conversation as resolved.
jn:leading-4
`

// default size typography
const btnDefaultBase = `
jn:text-base
Expand All @@ -55,6 +60,11 @@ const btnSmallSubduedPadding = `
jn:px-1.75
`

const btnXSDefaultPadding = `
jn:py-1
jn:px-1.5
`

// default size padding
const btnDefaultPadding = `
jn:py-1.75
Expand All @@ -81,7 +91,7 @@ const primaryButtonColors = `
jn:hover:bg-theme-button-primary-hover
jn:hover:text-theme-button-primary-hover
jn:active:bg-theme-button-primary-active
jn_active:text-theme-button-primary-active
jn:active:text-theme-button-primary-active
`

const primaryDangerButtonColors = `
Expand All @@ -101,16 +111,15 @@ const subduedButtonColors = `
jn:active:bg-theme-button-subdued-active
jn:active:text-theme-button-subdued-active
`
// jn:border
// jn:border-theme-button-subdued
// jn:hover:border-theme-button-subdued-hover
// jn:active:border-theme-button-subdued-active

const getButtonPadding = (size: ButtonSize, variant: ButtonVariant | undefined) => {
if (size === "small") {
return variant === "subdued" ? `${btnSmallSubduedPadding}` : `${btnSmallDefaultPadding}`
} else {
return variant === "subdued" ? `${btnDefaultSubduedPadding}` : `${btnDefaultPadding}`
switch (size) {
case "small":
return variant === "subdued" ? `${btnSmallSubduedPadding}` : `${btnSmallDefaultPadding}`
case "xs":
return `${btnXSDefaultPadding}`
default:
return variant === "subdued" ? `${btnDefaultSubduedPadding}` : `${btnDefaultPadding}`
}
Comment thread
MartinS-git marked this conversation as resolved.
}

Expand All @@ -133,15 +142,44 @@ const btnIconSmall = `
jn:mr-2
`

const btnIconXS = `
jn:mr-1
`

const btnIconDefault = `
jn:mr-2
`

const getButtonBase = (size: ButtonSize) => {
switch (size) {
case "small":
return btnSmallBase
case "xs":
return btnXSBase
default:
return btnDefaultBase
}
}

const iconClasses = (size: ButtonSize) => {
if (size === "small") {
return `${btnIconSmall}`
} else {
return `${btnIconDefault}`
switch (size) {
case "small":
return `${btnIconSmall}`
case "xs":
return `${btnIconXS}`
default:
return `${btnIconDefault}`
}
}

const iconSize = (size: ButtonSize) => {
switch (size) {
case "small":
return "1.125rem"
case "xs":
return "1rem"
default:
return "1.5rem"
}
}

Expand Down Expand Up @@ -192,13 +230,17 @@ export const Button = forwardRef<HTMLButtonElement | HTMLAnchorElement, ButtonPr
const titleValue = title || label || ""

const buttonIcon = progress ? (
<Spinner size={size === "small" ? "1.125rem" : "1.5rem"} color={spinnerColorClass(variant)} />
<Spinner
size={`${iconSize(size)}`}
color={spinnerColorClass(variant)}
className={`${label || children ? iconClasses(size) : ""}`}
/>
Comment thread
MartinS-git marked this conversation as resolved.
) : icon ? (
<Icon
icon={icon}
title={titleValue}
className={`juno-button-icon ${label || children ? iconClasses(size) : ""} `}
size={size === "small" ? "1.125rem" : "1.5rem"}
size={`${iconSize(size)}`}
/>
) : null

Expand All @@ -215,7 +257,7 @@ export const Button = forwardRef<HTMLButtonElement | HTMLAnchorElement, ButtonPr
juno-button-${variant}
juno-button-${size}-size
${btnBase}
${size === "small" ? btnSmallBase : btnDefaultBase}
${getButtonBase(size)}
${getButtonPadding(size, variant)}
${getButtonColors(variant)}
${progressClass(progress)}
Expand All @@ -241,7 +283,7 @@ export const Button = forwardRef<HTMLButtonElement | HTMLAnchorElement, ButtonPr
juno-button-${variant}
juno-button-${size}-size
${btnBase}
${size === "small" ? btnSmallBase : btnDefaultBase}
${getButtonBase(size)}
${getButtonPadding(size, variant)}
${getButtonColors(variant)}
${progressClass(progress)}
Expand All @@ -265,7 +307,7 @@ export const Button = forwardRef<HTMLButtonElement | HTMLAnchorElement, ButtonPr
Button.displayName = "Button"

export type ButtonVariant = "primary" | "primary-danger" | "default" | "subdued"
type ButtonSize = "small" | "default"
type ButtonSize = "small" | "xs" | "default"

Comment thread
MartinS-git marked this conversation as resolved.
export interface ButtonProps extends Omit<HTMLProps<HTMLAnchorElement> | HTMLProps<HTMLButtonElement>, "size"> {
/**
Expand Down
42 changes: 40 additions & 2 deletions packages/ui-components/src/components/Button/Button.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ export const Small: Story = {
},
}

export const XS: Story = {
args: {
size: "xs",
label: "XS",
},
}

export const Disabled: Story = {
parameters: {
docs: {
Expand Down Expand Up @@ -215,6 +222,21 @@ export const SmallWithIcon: Story = {
},
}

export const XSWithIcon: Story = {
parameters: {
docs: {
description: {
story: "XS Button with Icon",
},
},
},

args: {
...XS.args,
icon: "warning",
},
}

export const IconOnlyButton: Story = {
parameters: {
docs: {
Expand Down Expand Up @@ -312,7 +334,7 @@ export const DefaultButtonInProgressWithProgressLabel: Story = {
parameters: {
docs: {
description: {
story: "Default Button with an action in oprogress and an alternate label while in progress",
story: "Default Button with an action in progress and an alternate label while in progress",
},
},
},
Expand Down Expand Up @@ -426,7 +448,7 @@ export const PrimaryDisabledInProgress: Story = {
parameters: {
docs: {
description: {
story: "Disabled Primnary Button with action in progress",
story: "Disabled Primary Button with action in progress",
},
},
},
Expand Down Expand Up @@ -469,3 +491,19 @@ export const SmallInProgress: Story = {
progressLabel: "Small in Progress…",
},
}

export const XSInProgress: Story = {
parameters: {
docs: {
description: {
story: "XS Button in Progress",
},
},
},

args: {
...XS.args,
progress: true,
progressLabel: "XS in Progress…",
},
}
17 changes: 17 additions & 0 deletions packages/ui-components/src/components/Button/Button.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,21 @@ describe("Button", () => {
expect(screen.getByRole("img")).toBeInTheDocument()
expect(screen.getByRole("img")).toContainHTML("<title>customTitle</title>")
})

test("renders an xs button", () => {
render(<Button size="xs">Click me</Button>)
expect(screen.getByRole("button")).toBeInTheDocument()
expect(screen.getByRole("button")).toHaveClass("juno-button-xs-size")
})

test("renders an xs in progress button as passed", () => {
render(
<Button size="xs" progress={true}>
Click me
</Button>
)
expect(screen.getByRole("button")).toBeInTheDocument()
expect(screen.getByRole("button")).toHaveClass("in-progress")
expect(screen.getByRole("progressbar")).toHaveClass("juno-spinner")
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -53,25 +53,23 @@ export const ComplexCustomLayout: Story = {
<DataGridToolbar {...args}>
<Stack direction="horizontal" distribution="between">
<Stack direction="vertical" gap="4">
<Stack alignment="center" gap="4">
<InputGroup>
<NativeSelect name="Filter" value="category" wrapperClassName="jn:w-full">
<NativeSelectOption value="category" label="Category" />
<NativeSelectOption value="status" label="Status" />
<NativeSelectOption value="priority" label="Priority" />
</NativeSelect>
<ComboBox>
<ComboBoxOption value="Electronics" />
<ComboBoxOption value="Clothing" />
<ComboBoxOption value="Furniture" />
</ComboBox>
</InputGroup>
<Button label="Clear all" variant="subdued" />
</Stack>
<InputGroup>
<NativeSelect name="Filter" value="category" wrapperClassName="jn:w-full">
<NativeSelectOption value="category" label="Category" />
<NativeSelectOption value="status" label="Status" />
<NativeSelectOption value="priority" label="Priority" />
</NativeSelect>
<ComboBox>
<ComboBoxOption value="Electronics" />
<ComboBoxOption value="Clothing" />
<ComboBoxOption value="Furniture" />
</ComboBox>
</InputGroup>
<Stack gap="2" wrap>
<Pill pillKey="category" pillValue="electronics" closeable />
<Pill pillKey="status" pillValue="active" closeable />
<Pill pillKey="priority" pillValue="high" closeable />
<Button label="Clear Filters" variant="subdued" size="xs" />
Comment thread
MartinS-git marked this conversation as resolved.
</Stack>
Comment thread
MartinS-git marked this conversation as resolved.
</Stack>
<SearchInput placeholder="Search items..." />
Expand All @@ -85,7 +83,7 @@ export const ComplexCustomLayout: Story = {
docs: {
description: {
story:
"Demonstrates a complex toolbar layout with custom styling - children aligned left and search aligned right.",
"Demonstrates a complex toolbar layout with custom styling - children aligned left and search aligned right. The xs 'Clear Filters' button sits inline with the filter pills.",
},
},
},
Expand Down
Loading