Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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/card-hover-active-states.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cloudoperators/juno-ui-components": minor
---

feat(Card): add hover and active states for interactive cards. When rendered as `<a>` or `<button>`, the Card now shows an elevated border on hover (`border-theme-card-hover`) with an enhanced shadow, and an accent border on active. Adds `--color-border-high` and `--color-card-border-hover` CSS variables.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ const cardStyles = `
jn:shadow-theme-default
`

const cardInteractiveStyles = `
Comment thread
MartinS-git marked this conversation as resolved.
jn:cursor-pointer
jn:hover:border-theme-card-hover
jn:hover:shadow-theme-default-hover
jn:active:border-theme-accent
Comment thread
MartinS-git marked this conversation as resolved.
`

const cardPadding = "jn:p-4"

export interface CardProps extends HTMLAttributes<HTMLElement> {
Expand Down Expand Up @@ -60,7 +67,8 @@ export interface CardProps extends HTMLAttributes<HTMLElement> {
*/
export const Card = forwardRef<HTMLElement, CardProps>(
({ children, padding = false, className = "", href, onClick, disabled = false, ...props }, ref) => {
const combinedClassName = `juno-card ${cardStyles} ${padding ? cardPadding : ""} ${className} ${disabled ? "jn:cursor-not-allowed" : ""}`
const isInteractive = (!!href || !!onClick) && !disabled
const combinedClassName = `juno-card ${cardStyles} ${isInteractive ? cardInteractiveStyles : ""} ${padding ? cardPadding : ""} ${className} ${disabled ? "jn:cursor-not-allowed" : ""}`
Comment thread
MartinS-git marked this conversation as resolved.
Outdated

// Handle click event when disabled
const handleClick: MouseEventHandler<HTMLElement> = (event) => {
Expand Down
8 changes: 8 additions & 0 deletions packages/ui-components/src/components/Card/Card.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,11 @@ export const WithoutPadding: Story = {
onClick: undefined,
},
}

export const InteractiveHover: Story = {
Comment thread
MartinS-git marked this conversation as resolved.
Outdated
args: {
children: "Hover or click me to see the interactive states.",
padding: true,
onClick: () => {},
},
}
48 changes: 48 additions & 0 deletions packages/ui-components/src/components/Card/Card.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,52 @@ describe("Card Component", () => {
expect(handleClick).not.toHaveBeenCalled()
})
})

describe("Interactive Styles", () => {
test("applies interactive styles when onClick is provided", () => {
Comment thread
MartinS-git marked this conversation as resolved.
render(
<Card data-testid="card" onClick={() => {}}>
Button
</Card>
)
const element = screen.getByTestId("card")
expect(element).toHaveClass("jn:cursor-pointer")
expect(element).toHaveClass("jn:hover:border-theme-card-hover")
expect(element).toHaveClass("jn:hover:shadow-theme-default-hover")
expect(element).toHaveClass("jn:active:border-theme-accent")
})

test("applies interactive styles when href is provided", () => {
render(
<Card data-testid="card" href="https://example.com">
Link
</Card>
)
const element = screen.getByTestId("card")
expect(element).toHaveClass("jn:cursor-pointer")
expect(element).toHaveClass("jn:hover:border-theme-card-hover")
expect(element).toHaveClass("jn:hover:shadow-theme-default-hover")
expect(element).toHaveClass("jn:active:border-theme-accent")
})

test("does not apply interactive styles when neither href nor onClick are provided", () => {
render(<Card data-testid="card">Div</Card>)
const element = screen.getByTestId("card")
expect(element).not.toHaveClass("jn:cursor-pointer")
expect(element).not.toHaveClass("jn:hover:border-theme-card-hover")
expect(element).not.toHaveClass("jn:active:border-theme-accent")
})

test("does not apply interactive styles when disabled", () => {
render(
<Card data-testid="card" onClick={() => {}} disabled>
Disabled
</Card>
)
const element = screen.getByTestId("card")
expect(element).not.toHaveClass("jn:cursor-pointer")
expect(element).not.toHaveClass("jn:hover:border-theme-card-hover")
expect(element).toHaveClass("jn:cursor-not-allowed")
})
})
})
45 changes: 31 additions & 14 deletions packages/ui-components/src/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@
--border-color-theme-box-default: var(--color-box-border);

--border-color-theme-card-default: var(--color-card-border-default);
--border-color-theme-card-hover: var(--color-card-border-hover);

Comment thread
MartinS-git marked this conversation as resolved.
--border-color-theme-codeblock-bar: var(--color-codeblock-bar-border);
--border-color-theme-codeblock-tab-active: var(--color-text-default);
Expand Down Expand Up @@ -532,6 +533,7 @@
/* LT Global */
--color-global-bg: var(--color-juno-grey-light-1);
--color-border-default: var(--color-juno-grey-light-7);
--color-border-high: var(--color-juno-grey-light-9);
--color-global-text: var(--color-juno-text-default);
--color-content-area-bg: var(--color-background-lvl-0);
/* LT FOCUS */
Expand Down Expand Up @@ -595,7 +597,7 @@
--color-button-primary-danger-active-text: var(--color-white);
--color-button-primary-danger-active-bg: var(--color-juno-primary-danger-5);

/* LT DataGrid */
/* LT DataGrid */
--color-datagridtoolbar-bg: var(--color-background-lvl-1);
--color-datagridrow-selected: var(--color-accent);

Expand Down Expand Up @@ -650,6 +652,7 @@
/* LT Card */
--color-card-background-default: var(--color-white);
--color-card-border-default: var(--color-border-default);
--color-card-border-hover: var(--color-border-high);
/* LT Checkbox */
--color-checkbox-bg: var(--color-background-lvl-5);
--color-checkbox-checked-color: var(--color-accent);
Expand Down Expand Up @@ -742,7 +745,7 @@
); /* Dark Theme DateTimePicker range background. We need to use a synthetisized solid value based on the current theme's accent color, the way that flatpickr is built here forbids using translucent colors. */
/* LT Box Shadow */
--box-shadow-default: 0 1px 2px 0 rgba(34, 54, 73, 0.3);
--box-shadow-hover: 0 0 0 1px var(--color-color-shadow, rgba(79, 79, 79, 0.3)), 0 2px 8px 0 rgba(0, 0, 0, 0.3);
--box-shadow-hover: 0 0 0 1px var(--color-color-shadow, rgba(79, 79, 79, 0.2)), 0 2px 8px 0 rgba(0, 0, 0, 0.2);
Comment thread
MartinS-git marked this conversation as resolved.
Comment thread
MartinS-git marked this conversation as resolved.
/* LT PageFooter */
--color-pagefooter-text: var(--color-text-light);
--color-pagefooter-background: var(--color-white);
Expand Down Expand Up @@ -783,6 +786,7 @@
/* DT Global */
--color-global-bg: var(--color-juno-grey-blue-10);
--color-border-default: var(--color-juno-grey-blue-3);
--color-border-high: var(--color-juno-grey-blue-1);
--color-global-text: var(--color-juno-text-default);
--color-content-area-bg: var(--color-background-lvl-0);

Expand Down Expand Up @@ -901,6 +905,7 @@
/* DT Card */
--color-card-background-default: var(--color-juno-grey-blue-10);
--color-card-border-default: var(--color-border-default);
--color-card-border-hover: var(--color-border-high);
/* DT Checkbox */
--color-checkbox-bg: var(--color-background-lvl-5);
--color-checkbox-checked-color: var(--color-accent);
Expand Down Expand Up @@ -1052,41 +1057,53 @@
@apply jn:text-theme-link;
}

h1, .juno-h1,
h2, .juno-h2,
h3, .juno-h3,
h4, .juno-h4,
h5, .juno-h5,
h6, .juno-h6 {
h1,
.juno-h1,
h2,
.juno-h2,
h3,
Comment thread
MartinS-git marked this conversation as resolved.
.juno-h3,
h4,
.juno-h4,
h5,
.juno-h5,
h6,
.juno-h6 {
@apply jn:font-sans jn:font-bold;
}

h1, .juno-h1 {
h1,
.juno-h1 {
font-size: 1.69rem;
line-height: 1.375;
}

h2, .juno-h2 {
h2,
.juno-h2 {
font-size: 1.56rem;
line-height: 1.375;
}

h3, .juno-h3 {
h3,
.juno-h3 {
font-size: 1.44rem;
line-height: 1.375;
}

h4, .juno-h4 {
h4,
.juno-h4 {
font-size: 1.28rem;
line-height: 1.625;
}

h5, .juno-h5 {
h5,
.juno-h5 {
font-size: 1.125rem;
line-height: 1.625;
}

h6, .juno-h6 {
h6,
.juno-h6 {
font-size: 1rem;
line-height: 1.5;
}
Expand Down
7 changes: 6 additions & 1 deletion packages/ui-components/src/theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@
--border-color-theme-box-default: var(--color-box-border);

--border-color-theme-card-default: var(--color-card-border-default);
--border-color-theme-card-hover: var(--color-card-border-hover);

--border-color-theme-codeblock-bar: var(--color-codeblock-bar-border);
--border-color-theme-codeblock-tab-active: var(--color-text-default);
Expand Down Expand Up @@ -521,6 +522,7 @@
/* LT Global */
--color-global-bg: var(--color-juno-grey-light-1);
--color-border-default: var(--color-juno-grey-light-7);
--color-border-high: var(--color-juno-grey-light-9);
--color-global-text: var(--color-juno-text-default);
--color-content-area-bg: var(--color-background-lvl-0);
/* LT FOCUS */
Expand Down Expand Up @@ -635,6 +637,7 @@
/* LT Card */
--color-card-background-default: var(--color-white);
--color-card-border-default: var(--color-border-default);
--color-card-border-hover: var(--color-border-high);
/* LT Checkbox */
--color-checkbox-bg: var(--color-background-lvl-5);
--color-checkbox-checked-color: var(--color-accent);
Expand Down Expand Up @@ -730,7 +733,7 @@
); /* Light Theme DateTimePicker range background. We need to use a synthetisized solid value based on the current theme's accent color, the way that flatpickr is built here forbids using translucent colors. */
/* LT Box Shadow */
--box-shadow-default: 0 1px 2px 0 rgba(34, 54, 73, 0.3);
--box-shadow-hover: 0 0 0 1px var(--color-color-shadow, rgba(79, 79, 79, 0.3)), 0 2px 8px 0 rgba(0, 0, 0, 0.3);
--box-shadow-hover: 0 0 0 1px var(--color-color-shadow, rgba(79, 79, 79, 0.2)), 0 2px 8px 0 rgba(0, 0, 0, 0.2);
/* LT PageFooter */
--color-pagefooter-text: var(--color-text-light);
--color-pagefooter-background: var(--color-white);
Expand Down Expand Up @@ -772,6 +775,7 @@
/* DT Global */
--color-global-bg: var(--color-juno-grey-blue-10);
--color-border-default: var(--color-juno-grey-blue-3);
--color-border-high: var(--color-juno-grey-blue-1);
--color-global-text: var(--color-juno-text-default);
--color-content-area-bg: var(--color-background-lvl-0);

Expand Down Expand Up @@ -888,6 +892,7 @@
/* DT Card */
--color-card-background-default: var(--color-juno-grey-blue-10);
--color-card-border-default: var(--color-border-default);
--color-card-border-hover: var(--color-border-high);
/* DT Checkbox */
--color-checkbox-bg: var(--color-background-lvl-5);
--color-checkbox-checked-color: var(--color-accent);
Expand Down
Loading