Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: new studio component StudioIconCard #14844

Merged
merged 1 commit into from
Mar 6, 2025
Merged

feat: new studio component StudioIconCard #14844

merged 1 commit into from
Mar 6, 2025

Conversation

Jondyr
Copy link
Member

@Jondyr Jondyr commented Mar 3, 2025

Component that displays a card with an icon, description and optional
ellipsis menu.
Created to be used on the new navigation page for Form Designer.


Stack:

⚠️ Part of a stack created by spr. Do not merge manually using the UI - doing so may have unexpected results.

Summary by CodeRabbit

  • New Features
    • Introduced an interactive card component that elegantly displays an icon, header, and associated content.
    • Enhanced with responsive hover effects that reveal additional interactive options.
    • Supports customizable color themes for a consistent, visually engaging experience.

Copy link
Contributor

coderabbitai bot commented Mar 3, 2025

📝 Walkthrough

Walkthrough

This pull request introduces a new StudioIconCard feature, which includes a CSS module for styling and a React component for rendering. The CSS module defines styles for the card, including layout, hover effects, and color utilities. The React component accepts various props and utilizes the CSS module for styling, while also exporting types for props. Additionally, a test suite for the component is added, along with a new constant for a popover trigger.

Changes

Files Change Summary
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css, frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx Added new StudioIconCard functionality. The CSS module defines card layout, hover transitions, icon positioning, and color utilities. The TSX file implements the React component with props for icon, header, context buttons, and exports associated types.
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx Added a test suite for the StudioIconCard component, including tests for rendering children, the icon prop, and a clickable popover trigger.
frontend/testing/testids.js Added a new constant studioIconCardPopoverTrigger for the popover trigger.

Suggested labels

skip-releasenotes, team/studio-domain1

Suggested reviewers

  • github-actions
  • wrt95

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the plugin "eslint-plugin-storybook".

(The package "eslint-plugin-storybook" was not found when loaded as a Node module from the directory "/frontend/libs/studio-components".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-storybook@latest --save-dev

The plugin "eslint-plugin-storybook" was referenced from the config file in "frontend/libs/studio-components/.eslintrc.js".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.

frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the plugin "eslint-plugin-storybook".

(The package "eslint-plugin-storybook" was not found when loaded as a Node module from the directory "/frontend/libs/studio-components".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-storybook@latest --save-dev

The plugin "eslint-plugin-storybook" was referenced from the config file in "frontend/libs/studio-components/.eslintrc.js".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.

frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the plugin "eslint-plugin-storybook".

(The package "eslint-plugin-storybook" was not found when loaded as a Node module from the directory "/frontend/libs/studio-components".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-storybook@latest --save-dev

The plugin "eslint-plugin-storybook" was referenced from the config file in "frontend/libs/studio-components/.eslintrc.js".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added solution/studio/designer Issues related to the Altinn Studio Designer solution. frontend labels Mar 3, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (1)

10-18: Consider exporting the StudioIconCardProps type.

The component exports StudioIconCardIconColors but not the main StudioIconCardProps type. If other components need to reference this type for composition or extension, exporting it would be helpful.

-type StudioIconCardProps = {
+export type StudioIconCardProps = {
  icon: ReactElement;
  iconColor?: StudioIconCardIconColors;
  linkHref?: string;
  header?: string;
  headerOptions?: HeadingProps;
  description?: string;
  descriptionOptions?: ParagraphProps;
};
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6a8819a and 27cb4ca.

📒 Files selected for processing (2)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css (1 hunks)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Build environment and run e2e test
🔇 Additional comments (5)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (5)

1-7: Imports look good and well-organized.

The imports are cleanly organized and include all necessary dependencies for the component.


8-8: Type definition for icon colors is clear and well-constrained.

The exported type StudioIconCardIconColors provides a good set of color options and makes the component API clear to users.


20-28: Component props structure looks good.

The component definition with destructured props and default values for iconColor is clean and follows React best practices.


51-51: Overall implementation is clean and well-structured.

The component follows a good pattern for a reusable UI element, with appropriate props, styling integration, and component composition. With the suggested improvements for conditional rendering and accessibility, this will be a robust addition to the component library.


36-36: Make sure icon color classes exist in the CSS module.

The component relies on CSS classes that match the color names in StudioIconCardIconColors. Ensure that the CSS module includes corresponding classes for each color ('blue', 'red', 'green', 'grey', 'yellow').

#!/bin/bash
# Check that all required color classes exist in the CSS module
cat frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css | grep -E "\.(blue|red|green|grey|yellow)\s*\{"

@Jondyr Jondyr force-pushed the spr/main/fe161544 branch from 27cb4ca to 95b26fd Compare March 3, 2025 09:28
@github-actions github-actions bot added the quality/testing Tests that are missing, needs to be created or could be improved. label Mar 3, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (1)

55-57: 🛠️ Refactor suggestion

Add conditional rendering for header.

The component always renders a StudioHeading even when header might be undefined. This could lead to unnecessary empty elements in the DOM.

-        <StudioHeading className={classes.title} size='2xs' {...headerOptions}>
-          {header}
-        </StudioHeading>
+        {header && (
+          <StudioHeading className={classes.title} size='2xs' {...headerOptions}>
+            {header}
+          </StudioHeading>
+        )}
🧹 Nitpick comments (8)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (2)

9-17: Remove unnecessary async keyword.

The test doesn't contain any asynchronous operations, so the async keyword is unnecessary.

-  it('should render children as content', async () => {
+  it('should render children as content', () => {

19-27: Remove unnecessary async keyword.

This test doesn't contain any asynchronous operations, so the async keyword is unnecessary.

-  it('should render icon prop', async () => {
+  it('should render icon prop', () => {
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css (4)

14-21: Verify if hover styles should be commented out.

The hover styles are commented out. Is this intentional or should these be active? If they should remain commented out, consider removing them entirely or adding a comment explaining why they're preserved for future reference.

Would you like me to uncomment these styles or help you decide which hover styles to keep?


68-73: Use CSS variables for color values instead of hardcoded hex codes.

Hardcoded color values (#4b5563) are used instead of CSS variables. This may be inconsistent with the design system approach. Consider using semantic color variables for better maintainability.

.iconBackground svg,
.iconBackground img {
  transform: rotate(-45deg);
  font-size: var(--fds-sizing-9);
-  color: #4b5563;
+  color: var(--fds-semantic-text-neutral-default);
}

75-98: Use CSS variables for color values in color classes.

All color classes use hardcoded hex code (#23262a) for text color. Consider using semantic color variables for better maintainability and consistency with the design system.

.blue {
  background-color: var(--fds-colors-blue-100);
-  color: #23262a;
+  color: var(--fds-semantic-text-neutral-default);
}

.red {
  background-color: var(--fds-colors-red-100);
-  color: #23262a;
+  color: var(--fds-semantic-text-neutral-default);
}

.green {
  background-color: var(--fds-colors-green-200);
-  color: #23262a;
+  color: var(--fds-semantic-text-neutral-default);
}

.grey {
  background-color: var(--fds-colors-grey-200);
-  color: #23262a;
+  color: var(--fds-semantic-text-neutral-default);
}

.yellow {
  background-color: var(--fds-colors-yellow-200);
-  color: #23262a;
+  color: var(--fds-semantic-text-neutral-default);
}

1-12: Consider adding responsiveness to card dimensions.

The card has fixed dimensions (244px) which might limit its flexibility on different screen sizes. Consider adding responsive behavior or allowing dynamic sizing based on content.

You could use relative units or media queries to make the card more responsive:

.card {
-  height: 244px;
-  width: 244px;
-  min-width: 244px;
+  height: 244px;
+  width: 244px;
+  min-width: 244px;

+  @media (max-width: 768px) {
+    height: 200px;
+    width: 200px;
+    min-width: 200px;
+  }

  transition:
    0.2s transform,
    0.2s box-shadow;

  background-color: var(--fds-semantic-background-default);
  box-shadow: none;
}
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (2)

15-24: Consider adding additional props for flexibility.

The component could benefit from additional props to enhance its flexibility, such as an optional onClick handler for the card itself or the ability to customize the icon size.

export type StudioIconCardProps = {
  icon: ReactElement;
  iconColor?: StudioIconCardIconColors;
  header?: string;
  headerOptions?: HeadingProps;
  contextButtons?: ReactNode;
  children: ReactNode;
+  onClick?: React.MouseEventHandler<HTMLDivElement>;
+  className?: string;
};

26-33: Enhance component with spread props pattern.

The component could benefit from the spread props pattern to allow passing additional HTML attributes to the root element.

export const StudioIconCard = ({
  icon,
  iconColor = 'grey',
  header,
  headerOptions,
  contextButtons,
  children,
+  className,
+  ...rest
}: StudioIconCardProps) => {

Then update the return statement:

  return (
-    <StudioCard className={classes.card}>
+    <StudioCard className={cn(classes.card, className)} {...rest}>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 27cb4ca and 95b26fd.

📒 Files selected for processing (4)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css (1 hunks)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (1 hunks)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (1 hunks)
  • frontend/testing/testids.js (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Build environment and run e2e test
  • GitHub Check: Testing
  • GitHub Check: CodeQL
🔇 Additional comments (2)
frontend/testing/testids.js (1)

19-19: LGTM: Test ID constant follows naming convention.

The newly added test ID constant follows the established naming convention and is descriptive for its purpose of identifying the popover trigger in the StudioIconCard component.

frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (1)

29-40: Test is correctly checking popover functionality.

The test properly checks that clicking the popover trigger displays the context button. The use of async/await is appropriate here because it involves user interaction.

@Jondyr Jondyr force-pushed the spr/main/fe161544 branch from 95b26fd to 9de1874 Compare March 3, 2025 11:39
@github-actions github-actions bot added the area/ui-editor Area: Related to the designer tool for assembling app UI in Altinn Studio. label Mar 3, 2025
@Jondyr Jondyr force-pushed the spr/main/fe161544 branch 2 times, most recently from c4665ec to d11ac82 Compare March 3, 2025 11:41
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (2)

54-58: Add conditional rendering for the header.

The component currently renders the StudioHeading element regardless of whether a header prop is provided. This could lead to an empty heading element in the DOM when no header is specified.

      <div className={classes.content}>
-       <StudioHeading className={classes.title} size='2xs' {...headerOptions}>
-         {header}
-       </StudioHeading>
+       {header && (
+         <StudioHeading className={classes.title} size='2xs' {...headerOptions}>
+           {header}
+         </StudioHeading>
+       )}
        {children}
      </div>

50-52: Consider accessibility of icon element.

The icon element is rendered without any accessibility attributes. If the icon conveys important information, it should have appropriate accessibility attributes.

      <div className={classes.iconContainer}>
-       <div className={cn(classes.iconBackground, classes[iconColor])}>{icon}</div>
+       <div className={cn(classes.iconBackground, classes[iconColor])} aria-hidden="true">{icon}</div>
      </div>

If the icon represents important content that should be announced to screen readers, consider adding aria-label instead of aria-hidden="true".

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 95b26fd and d11ac82.

📒 Files selected for processing (4)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css (1 hunks)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (1 hunks)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (1 hunks)
  • frontend/testing/testids.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/testing/testids.js
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Build environment and run e2e test
  • GitHub Check: CodeQL
  • GitHub Check: Testing
🔇 Additional comments (6)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (6)

1-14: Well-structured imports with clear organization.

The imports are well-organized, separating React core imports, component imports, utility libraries, and constants. This follows good practices for import organization.


15-15: Good type definition for icon colors.

The StudioIconCardIconColors type provides a clear set of allowed color options that matches the design system palette. This helps enforce consistent styling across the application.


17-24: Props interface is well-designed with appropriate optionality.

The props interface is clear and properly typed, with good use of optionality for non-required props. The use of React's built-in types (ReactElement and ReactNode) is appropriate for the different prop requirements.


26-33: Component signature follows best practices.

The component declaration and props destructuring with default values follows React best practices. Setting a default value for iconColor ensures the component always has a valid appearance.


37-44: Add accessibility attributes to the popover trigger.

The popover trigger lacks accessibility attributes. Since this represents an action, it should have an accessible label for screen readers.

          <StudioPopoverTrigger
            data-testid={studioIconCardPopoverTrigger}
            variant='tertiary'
            className={classes.editIcon}
+            aria-label="Open menu"
          >
            <MenuElipsisVerticalIcon />
          </StudioPopoverTrigger>

34-60:

❓ Verification inconclusive

Verify keyboard navigation and focus handling.

The component includes interactive elements like the popover trigger, but it's not clear if proper keyboard navigation and focus management is implemented. Ensure the component is fully accessible via keyboard.


🏁 Script executed:

#!/bin/bash
# Check if the StudioPopoverTrigger and related components properly handle keyboard focus

# Check if StudioPopoverTrigger implements keyboard accessibility
rg -A 3 -B 3 "StudioPopoverTrigger" --glob "*.tsx" | grep -E "(onKeyDown|onKeyPress|tabIndex|role|aria-)" || echo "No keyboard event handlers found in StudioPopoverTrigger components"

# Check StudioPopover implementation for accessibility features
rg -A 10 "export const StudioPopover" --glob "*.tsx"

Length of output: 305


Accessibility: Verify Keyboard Navigation and Focus Handling

Based on the executed script, no explicit keyboard-related event handlers (e.g. onKeyDown, onKeyPress, tabIndex, aria- attributes) were found in the StudioPopoverTrigger component. This raises a concern regarding keyboard accessibility for interactive elements such as the popover trigger. Please confirm whether:

  • The component either renders with a native element (e.g., a <button>) that inherently supports keyboard navigation,
  • Or if explicit keyboard event handlers and appropriate ARIA attributes are implemented elsewhere in the component hierarchy to manage focus and keyboard interactions.

If neither is the case, consider adding the necessary accessibility attributes and testing keyboard navigation to ensure the component is fully compliant. Manual verification of these aspects is recommended to avoid accessibility regressions.

@Jondyr Jondyr force-pushed the spr/main/fe161544 branch from d11ac82 to cecbab7 Compare March 3, 2025 12:25
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (1)

56-59: 🛠️ Refactor suggestion

Add conditional rendering for the header.

The component currently renders the StudioHeading component even when the header prop is undefined, which results in an empty heading element in the DOM. This can lead to unnecessary elements and potential accessibility issues.

      <div className={classes.content}>
-        <StudioHeading className={classes.title} size='2xs' {...headerOptions}>
-          {header}
-        </StudioHeading>
+        {header && (
+          <StudioHeading className={classes.title} size='2xs' {...headerOptions}>
+            {header}
+          </StudioHeading>
+        )}
        {children}
      </div>
🧹 Nitpick comments (4)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (4)

15-15: Consider adding more color options to align with design system.

The current color options are limited to 'blue', 'red', 'green', 'grey', and 'yellow'. Consider expanding this to include other colors that might be needed for various form elements or status indicators, such as 'purple', 'orange', or 'teal', if they're part of your design system.


17-24: Add documentation comments to the props interface.

Adding JSDoc comments to the props interface would improve developer experience by providing clear descriptions of each prop when consuming the component.

export type StudioIconCardProps = {
+  /** The icon to display in the card */
  icon: ReactElement;
+  /** The background color of the icon (defaults to 'grey') */
  iconColor?: StudioIconCardIconColors;
+  /** The header text to display */
  header?: string;
+  /** Additional props to pass to the StudioHeading component */
  headerOptions?: HeadingProps;
+  /** Content to display in the context menu popover */
  contextButtons?: ReactNode;
+  /** Content to display in the card body */
  children: ReactNode;
};

1-13: Organize imports for better maintainability.

Consider organizing imports into groups: React imports, internal components, types, utilities, and constants. This will improve readability and maintainability as the codebase grows.

import React, { type ReactElement, type ReactNode } from 'react';
import {
  StudioCard,
  StudioHeading,
  StudioPopover,
  StudioPopoverContent,
  StudioPopoverTrigger,
} from '@studio/components';
+ import { MenuElipsisVerticalIcon } from '@studio/icons';
+ import type { HeadingProps } from '@digdir/designsystemet-react';
+ 
+ import { studioIconCardPopoverTrigger } from '@studio/testing/testids';
+ import cn from 'classnames';
import classes from './StudioIconCard.module.css';
- import cn from 'classnames';
- import type { HeadingProps } from '@digdir/designsystemet-react';
- import { MenuElipsisVerticalIcon } from '@studio/icons';
- import { studioIconCardPopoverTrigger } from '@studio/testing/testids';

38-43: Consider adding a reference to the popover content for better accessibility.

To improve accessibility, it's recommended to create a relationship between the trigger and the content it controls using aria-controls.

          <StudioPopoverTrigger
            data-testid={studioIconCardPopoverTrigger}
            variant='tertiary'
            className={classes.editIcon}
+            aria-label="Open menu"
+            aria-controls="icon-card-popover-content"
          >
            <MenuElipsisVerticalIcon />
          </StudioPopoverTrigger>
-          <StudioPopoverContent className={classes.popoverContent}>
+          <StudioPopoverContent id="icon-card-popover-content" className={classes.popoverContent}>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d11ac82 and cecbab7.

📒 Files selected for processing (4)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css (1 hunks)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (1 hunks)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (1 hunks)
  • frontend/testing/testids.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/testing/testids.js
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Build environment and run e2e test
  • GitHub Check: Testing
  • GitHub Check: CodeQL
🔇 Additional comments (2)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (2)

37-44: Add accessibility attributes to the popover trigger.

The popover trigger currently lacks accessibility attributes. Since this represents an action, it should have an accessible label for screen readers.

          <StudioPopoverTrigger
            data-testid={studioIconCardPopoverTrigger}
            variant='tertiary'
            className={classes.editIcon}
+            aria-label="Open menu"
          >
            <MenuElipsisVerticalIcon />
          </StudioPopoverTrigger>

26-64: The component implementation looks clean and well-structured.

The overall implementation of the StudioIconCard component follows good React practices:

  • Props are properly destructured
  • Default values are provided where appropriate
  • Conditional rendering is used for optional elements
  • CSS modules are used for styling
  • Proper ARIA attributes are applied to decorative elements

@Jondyr Jondyr force-pushed the spr/main/fe161544 branch from cecbab7 to 0c6e880 Compare March 3, 2025 12:43
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (1)

38-44: 🛠️ Refactor suggestion

Add accessibility attributes to the popover trigger.

The popover trigger lacks accessibility attributes that would help screen reader users understand its purpose.

          <StudioPopoverTrigger
            data-testid={studioIconCardPopoverTrigger}
            variant='tertiary'
            className={classes.editIcon}
+           aria-label="Open menu"
          >
            <MenuElipsisVerticalIcon />
          </StudioPopoverTrigger>
🧹 Nitpick comments (7)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (4)

9-17: Remove unnecessary async modifier.

This test function is marked as async but doesn't use any await operations, making the async modifier unnecessary.

-  it('should render children as content', async () => {
+  it('should render children as content', () => {

19-27: Remove unnecessary async modifier.

Similar to the previous test, this test function is marked as async but doesn't contain any await operations.

-  it('should render icon prop', async () => {
+  it('should render icon prop', () => {

22-26: Enhance test by verifying icon color application.

The test verifies that the icon renders but doesn't check if the 'red' iconColor prop is correctly applied to the icon container.

    render(
      <StudioIconCard icon={<ClipboardIcon data-testid={iconTestId} />} iconColor='red'>
        <div></div>
      </StudioIconCard>,
    );
    expect(screen.getByTestId(iconTestId)).toBeInTheDocument();
+   const iconBackground = screen.getByTestId(iconTestId).closest('div').parentElement;
+   expect(iconBackground).toHaveClass('red');

29-40: Verify initial popover state before clicking.

The test should verify that the context buttons are not visible initially, then check they appear after clicking the trigger. This ensures the popover behavior works correctly.

    render(
      <StudioIconCard contextButtons={contextButtons} icon={<ClipboardIcon />}>
        <div></div>
      </StudioIconCard>,
    );
+   // Verify button is not initially visible
+   expect(screen.queryByRole('button', { name: buttonText })).not.toBeInTheDocument();
+   
    await user.click(screen.getByTestId(studioIconCardPopoverTrigger));
    expect(screen.getByRole('button', { name: buttonText })).toBeInTheDocument();
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (3)

50-53: Consider moving aria-hidden to the icon itself.

If the icon is purely decorative, it's better to apply aria-hidden to the icon itself rather than its container.

      <div className={classes.iconContainer}>
-       <div className={cn(classes.iconBackground, classes[iconColor])} aria-hidden={true}>
-         {icon}
+       <div className={cn(classes.iconBackground, classes[iconColor])}>
+         {React.cloneElement(icon, { 'aria-hidden': true })}
        </div>
      </div>

56-60: Add conditional rendering for the header.

The component always renders the StudioHeading element even when no header text is provided, which can result in empty heading elements in the DOM. Consider conditionally rendering the heading only when header text is available.

      <div className={classes.content}>
-       <StudioHeading className={classes.title} size='2xs' {...headerOptions}>
-         {header}
-       </StudioHeading>
+       {header && (
+         <StudioHeading className={classes.title} size='2xs' {...headerOptions}>
+           {header}
+         </StudioHeading>
+       )}
        {children}
      </div>

26-33: Add className prop for better component composition.

The component doesn't expose a way to add additional CSS classes at the root level. Consider adding a className prop to allow consumers to customize the component's appearance.

export type StudioIconCardProps = {
  icon: ReactElement;
  iconColor?: StudioIconCardIconColors;
  header?: string;
  headerOptions?: HeadingProps;
  contextButtons?: ReactNode;
  children: ReactNode;
+ className?: string;
};

export const StudioIconCard = ({
  icon,
  iconColor = 'grey',
  header,
  headerOptions,
  contextButtons,
  children,
+ className,
}: StudioIconCardProps) => {
  return (
-   <StudioCard className={classes.card}>
+   <StudioCard className={cn(classes.card, className)}>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cecbab7 and 0c6e880.

📒 Files selected for processing (4)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css (1 hunks)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (1 hunks)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (1 hunks)
  • frontend/testing/testids.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/testing/testids.js
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Build environment and run e2e test
  • GitHub Check: Typechecking and linting
  • GitHub Check: Testing
  • GitHub Check: CodeQL
🔇 Additional comments (2)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (1)

8-8: Properly named test suite.

The test suite is correctly named to match the component being tested, which follows best practices for test suite naming.

frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (1)

15-24: Well-defined type definitions.

The type definitions for the component are clear, strongly typed, and provide good documentation for the component's API.

@Jondyr Jondyr force-pushed the spr/main/fe161544 branch 2 times, most recently from ddd9024 to ca5b007 Compare March 4, 2025 12:21
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (3)

9-17: Remove unnecessary async declaration in non-asynchronous test.

This test doesn't contain any asynchronous operations (no await statements), so the async keyword is unnecessary.

- it('should render children as content', async () => {
+ it('should render children as content', () => {

19-27: Remove unnecessary async and test the iconColor prop.

Two improvements for this test:

  1. The async keyword is unnecessary as there are no await statements
  2. The test passes iconColor='red' but doesn't verify that the color is actually applied
- it('should render icon prop', async () => {
+ it('should render icon prop with correct color', () => {
    const iconTestId = 'icon-test-id';
    render(
      <StudioIconCard icon={<ClipboardIcon data-testid={iconTestId} />} iconColor='red'>
        <div></div>
      </StudioIconCard>,
    );
    expect(screen.getByTestId(iconTestId)).toBeInTheDocument();
+   const iconElement = screen.getByTestId(iconTestId);
+   // Check that the icon has the red color class applied
+   expect(iconElement.closest('[class*="red"]')).not.toBeNull();
  });

29-40: Consider adding test for popover interaction behavior.

The test verifies that clicking the popover trigger displays the button, but it doesn't test the behavior of clicking outside to close the popover or pressing escape key to close it, which are common expectations for popover components.

You could expand this test or add another one to verify these common popover behaviors:

it('should close popover when clicking outside', async () => {
  const user = userEvent.setup();
  const buttonText = 'button-text';
  const contextButtons = <button>{buttonText}</button>;
  render(
    <>
      <div data-testid="outside-element">Outside</div>
      <StudioIconCard contextButtons={contextButtons} icon={<ClipboardIcon />}>
        <div>Content</div>
      </StudioIconCard>
    </>
  );
  
  // Open popover
  await user.click(screen.getByTestId(studioIconCardPopoverTrigger));
  expect(screen.getByRole('button', { name: buttonText })).toBeInTheDocument();
  
  // Click outside
  await user.click(screen.getByTestId('outside-element'));
  
  // Verify popover is closed (button is no longer visible)
  expect(screen.queryByRole('button', { name: buttonText })).not.toBeInTheDocument();
});
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ddd9024 and ca5b007.

📒 Files selected for processing (4)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css (1 hunks)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (1 hunks)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (1 hunks)
  • frontend/testing/testids.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/testing/testids.js
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx
🔇 Additional comments (1)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (1)

8-8: Test suite named correctly.

The test suite is properly named to match the component name, which aligns with best practices for test organization and readability.

@Jondyr Jondyr force-pushed the spr/main/fe161544 branch from ca5b007 to 9914b16 Compare March 5, 2025 06:56
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (1)

1-41: 🛠️ Refactor suggestion

Add tests for header and headerOptions props.

The component accepts header and headerOptions props, but there are no tests to verify these are rendered correctly. This was mentioned in a previous review but hasn't been addressed yet.

Add a new test case to verify the header rendering functionality:

it('should render header with custom options', async () => {
  const headerText = 'Test Header';
  const customClassName = 'custom-header-class';
  render(
    <StudioIconCard 
      icon={<ClipboardIcon />} 
      header={headerText}
      headerOptions={{ className: customClassName }}
    >
      <div>Content</div>
    </StudioIconCard>,
  );
  
  const header = screen.getByText(headerText);
  expect(header).toBeInTheDocument();
  expect(header).toHaveClass(customClassName);
});
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (1)

37-44: 🛠️ Refactor suggestion

Add accessibility attributes to the popover trigger.

The popover trigger lacks accessibility attributes. Since it represents an action, it should have an accessible label for screen readers.

        <StudioPopoverTrigger
          data-testid={studioIconCardPopoverTrigger}
          variant='tertiary'
          className={classes.editIcon}
+         aria-label="Open menu"
        >
          <MenuElipsisVerticalIcon />
        </StudioPopoverTrigger>
🧹 Nitpick comments (1)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (1)

19-27: Good test for icon rendering.

This test properly verifies that the icon prop is rendered within the component. It also implicitly tests that the iconColor prop is accepted, though it doesn't verify the actual color application.

Consider adding a specific test for the iconColor classes to verify that the correct CSS class is applied based on the iconColor prop value. This would ensure that the color styling works as expected.

 it('should render icon prop', async () => {
   const iconTestId = 'icon-test-id';
+  const iconColor = 'red';
   render(
-    <StudioIconCard icon={<ClipboardIcon data-testid={iconTestId} />} iconColor='red'>
+    <StudioIconCard icon={<ClipboardIcon data-testid={iconTestId} />} iconColor={iconColor}>
       <div></div>
     </StudioIconCard>,
   );
   expect(screen.getByTestId(iconTestId)).toBeInTheDocument();
+  // Verify the color class is applied to the icon background
+  const iconBackground = screen.getByTestId(iconTestId).closest('div')?.parentElement;
+  expect(iconBackground).toHaveClass(`red`);
 });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ca5b007 and 9914b16.

📒 Files selected for processing (4)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css (1 hunks)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (1 hunks)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (1 hunks)
  • frontend/testing/testids.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/testing/testids.js
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Build environment and run e2e test
  • GitHub Check: Testing
  • GitHub Check: Typechecking and linting
  • GitHub Check: CodeQL
🔇 Additional comments (5)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (2)

8-17: Good implementation of basic functionality test.

This test correctly validates that the children passed to the StudioIconCard component are properly rendered within the document.


29-40: Effective test for context button functionality.

This test correctly verifies the interactive behavior of the popover trigger for context buttons. Good use of userEvent to simulate clicking and then checking that the button becomes visible.

frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (3)

15-24: Well-defined type definitions.

The type definitions are clear and properly structured. The use of TypeScript for prop types enhances type safety and developer experience.


56-61: Add conditional rendering for the header element.

The component always renders the <StudioHeading> element even if header is undefined or empty, which could lead to unnecessary empty elements in the DOM.

Apply this improvement to conditionally render the header:

      <div className={classes.content}>
-       <StudioHeading className={classes.title} size='2xs' {...headerOptions}>
-         {header}
-       </StudioHeading>
+       {header && (
+         <StudioHeading className={classes.title} size='2xs' {...headerOptions}>
+           {header}
+         </StudioHeading>
+       )}
        {children}
      </div>

50-53: Good use of aria-hidden attribute.

The aria-hidden attribute on the icon background is a positive accessibility practice, as it prevents screen readers from trying to announce purely decorative elements.

@Jondyr Jondyr force-pushed the spr/main/fe161544 branch from 9914b16 to 9a3732c Compare March 5, 2025 07:33
@Jondyr Jondyr linked an issue Mar 5, 2025 that may be closed by this pull request
5 tasks
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (1)

37-44: 🛠️ Refactor suggestion

Add accessibility attributes to the popover trigger.

The popover trigger lacks accessibility attributes for screen readers to understand its purpose.

          <StudioPopoverTrigger
            data-testid={studioIconCardPopoverTrigger}
            variant='tertiary'
            className={classes.editIcon}
+           aria-label="Open menu"
          >
            <MenuElipsisVerticalIcon />
          </StudioPopoverTrigger>
🧹 Nitpick comments (4)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (3)

9-17: Simplify test by removing unnecessary async.

This test doesn't contain any asynchronous operations, so the async keyword isn't needed.

-  it('should render children as content', async () => {
+  it('should render children as content', () => {

19-27: Simplify test by removing unnecessary async.

Similar to the previous test, there are no asynchronous operations here, so the async keyword can be removed.

-  it('should render icon prop', async () => {
+  it('should render icon prop', () => {

29-41: Missing test cases for additional props.

The tests cover basic functionality but don't test the header and headerOptions props as suggested in a previous review comment.

Consider adding a test case for the header functionality:

it('should render header with provided options', () => {
  const header = 'Test Header';
  const headerClassName = 'custom-header-class';
  
  render(
    <StudioIconCard 
      icon={<ClipboardIcon />} 
      header={header}
      headerOptions={{ className: headerClassName }}
    >
      <div></div>
    </StudioIconCard>,
  );
  
  const headerElement = screen.getByText(header);
  expect(headerElement).toBeInTheDocument();
  expect(headerElement).toHaveClass(headerClassName);
});
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (1)

56-61: Add conditional rendering for header.

The header is currently always rendered even if it's undefined or empty, which could lead to unnecessary elements in the DOM.

      <div className={classes.content}>
-       <StudioHeading className={classes.title} size='2xs' {...headerOptions}>
-         {header}
-       </StudioHeading>
+       {header && (
+         <StudioHeading className={classes.title} size='2xs' {...headerOptions}>
+           {header}
+         </StudioHeading>
+       )}
        {children}
      </div>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9914b16 and 9a3732c.

📒 Files selected for processing (4)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css (1 hunks)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (1 hunks)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (1 hunks)
  • frontend/testing/testids.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css
  • frontend/testing/testids.js
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Build environment and run e2e test
  • GitHub Check: CodeQL
  • GitHub Check: Testing
🔇 Additional comments (7)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (2)

1-7: Clean and clear imports.

The necessary imports are correctly included, including React, testing libraries, and the component under test.


8-8: Appropriate describe block naming.

The describe block is named 'StudioIconCard', which properly matches the component name.

frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (5)

1-14: Well-organized imports with clear component dependencies.

The imports are well-structured, grouped appropriately, and correctly reference needed components and utilities.


15-24: Clear type definitions with appropriate optionals.

The types are well-defined with appropriate optional flags for props that aren't required. The StudioIconCardIconColors type provides a clear set of allowed values.


26-33: Clean component declaration with sensible defaults.

The component declaration is clean with proper destructuring and a sensible default for the iconColor prop.


50-54: Appropriate aria-hidden attribute on decorative icon.

The icon container correctly uses aria-hidden={true} to indicate that it's decorative and should be ignored by screen readers.


34-63: Consider adding keyboard navigation support.

The component lacks explicit keyboard navigation support for the popover trigger, which is important for accessibility.

As noted in a previous review, there are no explicit styles defined for focus states. Make sure the CSS module includes appropriate styles for :focus and :focus-visible to provide visual feedback when the component is navigated using a keyboard.

In the CSS file, add rules similar to:

.editIcon:focus-visible {
  outline: 2px solid var(--focus-color);
  outline-offset: 2px;
}

@Jondyr Jondyr force-pushed the spr/main/fe161544 branch 2 times, most recently from 12077fe to 0e76757 Compare March 5, 2025 08:37
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css (1)

29-31: 🛠️ Refactor suggestion

Add focus states for accessibility.

The CSS only handles hover state for the edit icon visibility, but for proper keyboard navigation accessibility, focus states should also be included.

.card:not(:hover) .editIcon {
  display: none;
}

+.card:focus-within .editIcon,
+.card:focus .editIcon {
+  display: block;
+}
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (2)

37-44: 🛠️ Refactor suggestion

Add accessibility attributes to the popover trigger.

The popover trigger currently lacks accessibility attributes. Since this represents an action, it should have an accessible label for screen readers.

          <StudioPopoverTrigger
            data-testid={studioIconCardPopoverTrigger}
            variant='tertiary'
            className={classes.editIcon}
+            aria-label="Open menu"
          >
            <MenuElipsisVerticalIcon />
          </StudioPopoverTrigger>

56-61: 🛠️ Refactor suggestion

Add conditional rendering for header.

The component should conditionally render the header element only when a header value is provided.

      <div className={classes.content}>
-        <StudioHeading className={classes.title} size='2xs' {...headerOptions}>
-          {header}
-        </StudioHeading>
+        {header && (
+          <StudioHeading className={classes.title} size='2xs' {...headerOptions}>
+            {header}
+          </StudioHeading>
+        )}
        {children}
      </div>
🧹 Nitpick comments (4)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css (2)

5-10: Consider making card dimensions responsive.

The current implementation uses fixed dimensions (250px) for both height and width, which could limit responsiveness on different screen sizes.

.card {
-  height: 250px;
-  width: 250px;
-  min-width: 250px;
+  height: var(--card-height, 250px);
+  width: var(--card-width, 250px);
+  min-width: var(--card-min-width, 250px);
  background-color: var(--fds-semantic-background-default);
}

104-106: Consider adding hover/active states for buttons.

The button styling should include hover and active states for better user feedback.

.content button {
  width: 100%;
+  transition: background-color 0.2s ease;
+}
+
+.content button:hover {
+  background-color: var(--fds-semantic-background-hover);
+}
+
+.content button:active {
+  background-color: var(--fds-semantic-background-active);
}
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (2)

42-50: Add test for empty header case.

Since I recommended adding conditional rendering for the header, it would be good to add a test for the case where no header is provided.

it('should not render header when not provided', async () => {
  render(
    <StudioIconCard icon={<ClipboardIcon />}>
      <div>Content</div>
    </StudioIconCard>,
  );
  // Assuming there's only one heading from the children content
  expect(screen.queryByRole('heading')).not.toBeInTheDocument();
});

1-51: Add test for different iconColor values.

The component supports multiple iconColor values, but the tests only check one case ('red'). Consider adding tests for other color variations.

it('should apply the correct iconColor class', async () => {
  const { rerender } = render(
    <StudioIconCard icon={<ClipboardIcon />} iconColor='blue'>
      <div>Content</div>
    </StudioIconCard>,
  );
  
  // Using a more specific selector to find the element with the blue class
  const blueIconBg = document.querySelector('[class*="iconBackground"][class*="blue"]');
  expect(blueIconBg).toBeInTheDocument();
  
  // Test another color
  rerender(
    <StudioIconCard icon={<ClipboardIcon />} iconColor='yellow'>
      <div>Content</div>
    </StudioIconCard>,
  );
  
  const yellowIconBg = document.querySelector('[class*="iconBackground"][class*="yellow"]');
  expect(yellowIconBg).toBeInTheDocument();
});
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9a3732c and 0e76757.

📒 Files selected for processing (4)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css (1 hunks)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (1 hunks)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (1 hunks)
  • frontend/testing/testids.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/testing/testids.js
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Build environment and run e2e test
  • GitHub Check: Testing
  • GitHub Check: CodeQL
🔇 Additional comments (10)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css (5)

1-3: Good use of CSS custom properties.

The use of CSS variables for defining icon font color is a good practice for maintainability and future theming.


41-55: LGTM! Good implementation of the icon container.

The icon container implementation properly centers icon content vertically, which is important for consistent UI presentation.


64-87: Good use of color utility classes.

The color utility classes provide a clean way to apply different background colors based on the iconColor prop.


94-102: Good use of flex layout.

The flex layout for content spacing and alignment is well-implemented, providing good visual hierarchy.


57-62:

❓ Verification inconclusive

Ensure SVG accessibility.

When rotating SVGs, ensure they remain accessible. Adding appropriate ARIA attributes can help screen readers properly interpret the content.


🏁 Script executed:

#!/bin/bash
# Check if any SVG icons being used have proper accessibility attributes

# Look for SVG components in the imports
echo "Checking for SVG icon imports and their accessibility attributes:"
grep -r "import.*Icon" --include="*.tsx" frontend/libs/studio-components/src/ | head -n 10

# Check if there are any aria-label or role attributes used with icons
echo -e "\nChecking for aria attributes with icons:"
grep -r "aria-label" --include="*.tsx" frontend/libs/studio-components/src/ | head -n 10

Length of output: 3677


Ensure SVG Accessibility in StudioIconCard

It appears that many components in the project already include ARIA attributes for icons. However, the CSS in StudioIconCard.module.css rotates SVGs, and it’s unclear if the corresponding TSX component sets the necessary accessibility attributes. Please confirm that in the StudioIconCard component (likely frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx):

  • If the SVG icons are decorative, add aria-hidden="true".
  • If they convey meaning, include role="img" along with an appropriate aria-label.

This will ensure that the visual transformation doesn’t hinder accessibility for assistive technologies.

frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (2)

15-24: Well-defined and exported types.

The exported types are well-defined and provide good type safety for the component props.


50-54: Good use of aria-hidden for decorative icon container.

The aria-hidden attribute on the icon background is good for accessibility, as it hides decorative elements from screen readers.

frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (3)

8-17: Good test coverage for rendering children.

The test properly verifies that children are rendered as content within the component.


19-27: Good test for icon rendering.

The test correctly verifies that the icon is rendered in the component with the specified iconColor.


29-40: Good test for popover functionality.

The test correctly verifies that the popover trigger is clickable and shows the context buttons when clicked.

@Jondyr Jondyr force-pushed the spr/main/fe161544 branch from 0e76757 to 0a919c4 Compare March 5, 2025 08:45
@Jondyr Jondyr force-pushed the spr/main/fe161544 branch from 0a919c4 to 680d1b9 Compare March 6, 2025 08:58
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (1)

56-60: Conditionally render the header to avoid empty headings.

The header prop is optional, but the component always renders a StudioHeading component even when no header is provided. This can result in an empty heading element, which isn't ideal for accessibility and semantic HTML.

      <div className={classes.content}>
-       <StudioHeading className={classes.title} size='2xs' {...headerOptions}>
-         {header}
-       </StudioHeading>
+       {header && (
+         <StudioHeading className={classes.title} size='2xs' {...headerOptions}>
+           {header}
+         </StudioHeading>
+       )}
        {children}
      </div>
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (2)

8-50: Enhance test coverage for component variations.

The test suite covers the basic functionality well but misses a few important test cases:

  1. Testing what happens when header is not provided
  2. Testing different iconColor values to ensure correct styling
  3. Testing the component when contextButtons is not provided

Consider adding these additional test cases to ensure comprehensive coverage:

it('should not render header when not provided', () => {
  render(
    <StudioIconCard icon={<ClipboardIcon />}>
      <div>Content</div>
    </StudioIconCard>,
  );
  expect(screen.queryByRole('heading')).not.toBeInTheDocument();
});

it('should apply the correct color class based on iconColor', () => {
  const { container } = render(
    <StudioIconCard icon={<ClipboardIcon />} iconColor="blue">
      <div>Content</div>
    </StudioIconCard>,
  );
  // This would need to be adapted based on your actual CSS class implementation
  const iconBackground = container.querySelector('[class*="iconBackground"]');
  expect(iconBackground).toHaveClass('blue');
});

9-17: Remove unnecessary async declarations from tests without await operations.

Several test functions are declared as async but don't use any await operations, which is unnecessary and potentially misleading.

- it('should render children as content', async () => {
+ it('should render children as content', () => {
- it('should render icon prop', async () => {
+ it('should render icon prop', () => {
- it('should apply headerOptions to header studioheading', async () => {
+ it('should apply headerOptions to header studioheading', () => {

Also applies to: 19-27, 42-50

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e76757 and 680d1b9.

📒 Files selected for processing (4)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css (1 hunks)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (1 hunks)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (1 hunks)
  • frontend/testing/testids.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/testing/testids.js
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Build environment and run e2e test
  • GitHub Check: Testing
  • GitHub Check: CodeQL
🔇 Additional comments (1)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (1)

37-44: Add accessibility attributes to the popover trigger.

The popover trigger lacks an aria-label, which is essential for screen readers to understand the purpose of this interactive element.

          <StudioPopoverTrigger
            data-testid={studioIconCardPopoverTrigger}
            variant='tertiary'
            className={classes.editIcon}
+           aria-label="Open menu"
          >
            <MenuElipsisVerticalIcon />
          </StudioPopoverTrigger>

@JamalAlabdullah JamalAlabdullah self-assigned this Mar 6, 2025
Copy link
Contributor

@JamalAlabdullah JamalAlabdullah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good 👍

@JamalAlabdullah JamalAlabdullah removed their assignment Mar 6, 2025
Component that displays a card with an icon, description and optional
ellipsis menu.
Created to be used on the new navigation page for Form Designer.

commit-id:fe161544
@Jondyr Jondyr force-pushed the spr/main/fe161544 branch from 680d1b9 to 1bbf422 Compare March 6, 2025 10:47
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (2)

56-59: Consider conditional rendering for the header.

The header element is always rendered even when the header prop is undefined or empty, which can result in an empty heading element in the DOM. This might cause accessibility or layout issues.

      <div className={classes.content}>
-       <StudioHeading className={classes.title} size='2xs' {...headerOptions}>
-         {header}
-       </StudioHeading>
+       {header && (
+         <StudioHeading className={classes.title} size='2xs' {...headerOptions}>
+           {header}
+         </StudioHeading>
+       )}
        {children}
      </div>

17-24: Enhance component API with additional descriptive props.

The current implementation allows children but doesn't provide a dedicated prop for description text, which was mentioned in the PR objectives. This could make the component API less intuitive.

export type StudioIconCardProps = {
  icon: ReactElement;
  iconColor?: StudioIconCardIconColors;
  header?: string;
  headerOptions?: HeadingProps;
  contextButtons?: ReactNode;
+ description?: string;
+ descriptionOptions?: React.HTMLAttributes<HTMLParagraphElement>;
  children: ReactNode;
};

This enhancement would make the component more aligned with the PR objectives which mentioned a card with "icon, description, and an optional ellipsis menu".

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 680d1b9 and 1bbf422.

📒 Files selected for processing (4)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css (1 hunks)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx (1 hunks)
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (1 hunks)
  • frontend/testing/testids.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/testing/testids.js
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.module.css
  • frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.test.tsx
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Build environment and run e2e test
  • GitHub Check: CodeQL
  • GitHub Check: Testing
🔇 Additional comments (4)
frontend/libs/studio-components/src/components/StudioIconCard/StudioIconCard.tsx (4)

37-44: Add accessibility attributes to the popover trigger.

The popover trigger lacks an aria-label, which is essential for screen reader users to understand its purpose. Since this represents an action, it should have an accessible name.

        <StudioPopoverTrigger
          data-testid={studioIconCardPopoverTrigger}
          variant='tertiary'
          className={classes.editIcon}
+         aria-label="Open menu"
        >
          <MenuElipsisVerticalIcon />
        </StudioPopoverTrigger>

1-13: The import section is clean and well-organized.

All necessary imports are included with a logical grouping. The component correctly imports from the design system and local modules.


15-15: Clear and well-defined type for icon colors.

The type definition for StudioIconCardIconColors is clear and follows the naming conventions used in the codebase.


50-53: Good accessibility practice with aria-hidden attribute.

Setting aria-hidden={true} on the icon container is a good practice as it prevents screen readers from attempting to announce decorative elements.

@Jondyr Jondyr added the skip-manual-testing PRs that do not need to be tested manually label Mar 6, 2025
@Jondyr Jondyr merged commit 649faba into main Mar 6, 2025
13 of 14 checks passed
@Jondyr Jondyr deleted the spr/main/fe161544 branch March 6, 2025 11:10
@Jondyr Jondyr self-assigned this Mar 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/ui-editor Area: Related to the designer tool for assembling app UI in Altinn Studio. frontend quality/testing Tests that are missing, needs to be created or could be improved. skip-manual-testing PRs that do not need to be tested manually solution/studio/designer Issues related to the Altinn Studio Designer solution. team/studio-domain2
Projects
Status: ✅ Done
Development

Successfully merging this pull request may close these issues.

Create a card bar to display the layout sets
2 participants