Skip to content

Commit

Permalink
chore(cards): rename aiLabel to decorator (#6509)
Browse files Browse the repository at this point in the history
* chore(cards): rename aiLabel to decorator

* feat(cards): update style and prop description

* feat(cards): style update

* feat(cards): style fix

* fix(cards): class names update

* chore(cards): update test cases

* fix(cards): remove popover style for decorator class

* fix(cards): remove popover style for decorator class
  • Loading branch information
sangeethababu9223 authored Dec 9, 2024
1 parent 2654860 commit 6e58e9d
Show file tree
Hide file tree
Showing 10 changed files with 217 additions and 114 deletions.
24 changes: 19 additions & 5 deletions packages/ibm-products-styles/src/components/Card/_card.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// LICENSE file in the root directory of this source tree.
//

// NOTE: Please do not remove the duplicate `slug` and `ai-label` classes. We need both until slug is fully deprecated
// NOTE: Please do not remove the duplicate `slug` and `decorator` classes. We need both until slug is fully deprecated

// Standard imports.
@use '@carbon/styles/scss/theme' as *;
Expand Down Expand Up @@ -144,25 +144,39 @@ $block-class: #{c4p-settings.$pkg-prefix}--card;
position: relative;
}

.#{$block-class} .#{c4p-settings.$carbon-prefix}--slug {
.#{$block-class} .#{c4p-settings.$carbon-prefix}--slug,
.#{$block-class} .#{c4p-settings.$carbon-prefix}--ai-label {
position: absolute;
top: $spacing-05;
right: $spacing-05;
}

.#{$block-class}__header__inner-wrapper--decorator {
position: absolute;
top: $spacing-05;
right: $spacing-05;

.#{c4p-settings.$carbon-prefix}--slug,
.#{c4p-settings.$carbon-prefix}--ai-label {
position: relative;
top: unset;
right: unset;
}
}

.#{$block-class}__header-container--has-slug,
.#{$block-class}__header-container--has-ai-label {
.#{$block-class}__header-container--has-decorator {
width: 100%;
padding-right: $spacing-07;
}

.#{$block-class}__header-container--has-slug.#{$block-class}__header-container--has-actions,
.#{$block-class}__header-container--has-ai-label.#{$block-class}__header-container--has-actions {
.#{$block-class}__header-container--has-decorator.#{$block-class}__header-container--has-actions {
padding-right: $spacing-08;
}

.#{$block-class}__header-container--has-slug.#{$block-class}__header-container--large-tile-or-label,
.#{$block-class}__header-container--has-ai-label.#{$block-class}__header-container--large-tile-or-label {
.#{$block-class}__header-container--has-decorator.#{$block-class}__header-container--large-tile-or-label {
padding-right: $spacing-09;
}

Expand Down
25 changes: 14 additions & 11 deletions packages/ibm-products/src/components/Card/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,14 @@ interface CardProp extends PropsWithChildren {

/**
* Clickable tiles only accept a boolean value of true and display a hollow slug.
* @deprecated please use the `aiLabel` prop
* @deprecated please use the `decorator` prop
*/
slug?: ReactNode | boolean;

/**
* Optional prop that is intended for any scenario where something is being generated by AI to reinforce AI transparency, accountability, and explainability at the UI level.
* Optional prop that allows you to pass any component.
*/
aiLabel?: ReactNode | boolean;
decorator?: ReactNode | boolean;

status?: 'complete' | 'incomplete';
title?: ReactNode;
Expand All @@ -103,7 +103,7 @@ export const Card = forwardRef(
// The component props, in alphabetical order (for consistency).
actionIcons = Object.freeze([]),
actionsPlacement = 'bottom',
aiLabel,
decorator,
metadata = Object.freeze([]),
children,
className,
Expand Down Expand Up @@ -262,7 +262,10 @@ export const Card = forwardRef(
[`${blockClass}__clickable`]: clickable,
[`${blockClass}__media-left`]: mediaPosition === 'left',
[`${blockClass}--has-slug`]: !!slug,
[`${blockClass}--has-ai-label`]: !!aiLabel,
[`${blockClass}--has-decorator`]:
!!decorator && decorator['type']?.displayName !== 'AILabel',
[`${blockClass}--has-ai-label`]:
!!decorator && decorator['type']?.displayName === 'AILabel',
},
className
),
Expand All @@ -287,7 +290,7 @@ export const Card = forwardRef(

const getHeaderProps = () => ({
actions: actionsPlacement === 'top' ? getActions() : '',
aiLabel,
decorator,
noActionIcons:
getIcons().length > 0 && actionsPlacement === 'top' ? false : true,
actionsPlacement,
Expand Down Expand Up @@ -395,13 +398,13 @@ Card.propTypes = {
})
),
actionsPlacement: PropTypes.oneOf(['top', 'bottom']),
/**
* Optional prop that is intended for any scenario where something is being generated by AI to reinforce AI transparency, accountability, and explainability at the UI level.
*/
aiLabel: PropTypes.oneOfType([PropTypes.node, PropTypes.bool]),
children: PropTypes.node,
className: PropTypes.string,
clickZone: PropTypes.oneOf(['one', 'two', 'three']),
/**
* Optional prop that allows you to pass any component.
*/
decorator: PropTypes.oneOfType([PropTypes.node, PropTypes.bool]),
/**@ts-ignore */
description: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
disabled: PropTypes.bool,
Expand Down Expand Up @@ -455,7 +458,7 @@ Card.propTypes = {
/**
* **Experimental:** For all cases a `Slug` component can be provided.
* Clickable tiles only accept a boolean value of true and display a hollow slug.
* @deprecated please use the `aiLabel` prop
* @deprecated please use the `decorator` prop
*/
slug: PropTypes.oneOfType([PropTypes.node, PropTypes.bool]),

Expand Down
10 changes: 10 additions & 0 deletions packages/ibm-products/src/components/Card/CardHeader.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import { render } from '@testing-library/react';
import React from 'react';
import { CardHeader } from '.';
import { AILabel } from '@carbon/react';
import { pkg } from '../../settings';

const { name } = CardHeader;
Expand Down Expand Up @@ -50,4 +51,13 @@ describe(name, () => {
const { getByText } = render(<CardHeader {...props} />);
expect(getByText('action 1')).toBeVisible();
});

it('should respect decorator prop', () => {
const { container } = render(<CardHeader decorator={<AILabel />} />);
expect(
container.querySelector(
`.${blockClass}__header__inner-wrapper--decorator`
)
).not.toBeNull();
});
});
38 changes: 23 additions & 15 deletions packages/ibm-products/src/components/Card/CardHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ const defaults = {
interface CardHeaderProps {
actions?: ReactNode;
/**
* Optional prop that is intended for any scenario where something is being generated by AI to reinforce AI transparency, accountability, and explainability at the UI level.
* Optional prop that allows you to pass any component.
*/
aiLabel?: ReactNode | boolean;
decorator?: ReactNode | boolean;
description?: ReactNode;
hasActions?: boolean;
/**
Expand All @@ -46,7 +46,7 @@ interface CardHeaderProps {
/**
* **Experimental:** For all cases a `Slug` component can be provided.
* Clickable tiles only accept a boolean value of true and display a hollow slug.
* @deprecated please use the `aiLabel` prop
* @deprecated please use the `decorator` prop
*/
slug?: ReactNode;

Expand All @@ -56,7 +56,7 @@ interface CardHeaderProps {

export const CardHeader = ({
actions,
aiLabel,
decorator,
noActionIcons,
onPrimaryButtonClick,
onSecondaryButtonClick,
Expand Down Expand Up @@ -107,17 +107,17 @@ export const CardHeader = ({
</svg>
);

let normalizedAiLabel: React.ReactElement<any> | null = null;
if (aiLabel || slug) {
let normalizedDecorator: React.ReactElement<any> | null = null;
if (decorator || slug) {
if (
inClickableCard ||
typeof aiLabel === 'boolean' ||
typeof decorator === 'boolean' ||
typeof slug === 'boolean'
) {
normalizedAiLabel = hollowAiIcon;
normalizedDecorator = hollowAiIcon;
} else {
const element = aiLabel || slug;
normalizedAiLabel = React.cloneElement(
const element = decorator || slug;
normalizedDecorator = React.cloneElement(
element as React.ReactElement<any>,
{
size:
Expand All @@ -133,7 +133,7 @@ export const CardHeader = ({
className={cx([
`${headerClass}-container`,
{ [`${headerClass}-container--has-slug`]: !!slug },
{ [`${headerClass}-container--has-ai-label`]: !!aiLabel },
{ [`${headerClass}-container--has-decorator`]: !!decorator },
{ [`${headerClass}-container--has-actions`]: !!hasActions },
{
[`${headerClass}-container--large-tile-or-label`]:
Expand Down Expand Up @@ -180,7 +180,15 @@ export const CardHeader = ({
)}
</div>
)}
{normalizedAiLabel}
{slug ? (
normalizedDecorator
) : decorator ? (
<div className={`${blockClass}__header__inner-wrapper--decorator`}>
{normalizedDecorator}
</div>
) : (
''
)}
</div>
</div>
);
Expand All @@ -189,9 +197,9 @@ export const CardHeader = ({
CardHeader.propTypes = {
actions: PropTypes.oneOfType([PropTypes.array, PropTypes.node]),
/**
* Optional prop that is intended for any scenario where something is being generated by AI to reinforce AI transparency, accountability, and explainability at the UI level.
* Optional prop that allows you to pass any component.
*/
aiLabel: PropTypes.oneOfType([PropTypes.node, PropTypes.bool]),
decorator: PropTypes.oneOfType([PropTypes.node, PropTypes.bool]),
description: PropTypes.oneOfType([
PropTypes.string,
PropTypes.object,
Expand Down Expand Up @@ -219,7 +227,7 @@ CardHeader.propTypes = {
/**
* **Experimental:** For all cases a `Slug` component can be provided.
* Clickable tiles only accept a boolean value of true and display a hollow slug.
* @deprecated please use the `aiLabel` prop
* @deprecated please use the `decorator` prop
*/
slug: PropTypes.oneOfType([PropTypes.node, PropTypes.bool]),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const DocsPage = () => (
<ExpressiveCard
label="Label"
primaryButtonText="Primary"
aiLabel={<AILabel><AILabelContent>{renderedContent}</AILabelContent></AILabel>}
decorator={<AILabel><AILabelContent>{renderedContent}</AILabelContent></AILabel>}
title="Title">
<p>
expressive card body content block. description inviting the user to take action on the card.
Expand All @@ -64,7 +64,7 @@ const DocsPage = () => (
},
{
description:
'Clickable tiles only accept a boolean value of true for the aiLabel property.',
'Clickable tiles only accept a boolean value of true for the decorator property.',
source: {
language: 'html',
code: `
Expand All @@ -73,7 +73,7 @@ const DocsPage = () => (
primaryButtonText="Primary"
onClick={() => {}}
onKeyDown={() => {}}
aiLabel={true}
decorator={true}
title="Title">
<p>
expressive card body content block. description inviting the user to take action on the card.
Expand Down
Loading

0 comments on commit 6e58e9d

Please sign in to comment.