Skip to content

Commit

Permalink
feat(cards): renames slug to aiLabel (carbon-design-system#6167)
Browse files Browse the repository at this point in the history
* feat(cards): renames slug to aiLabel

* fix(card): replaced slug class with aiLabel

* feat(card): adds duplicate classname until deprecation

* fix(card): grouped duplicate css classes

---------

Co-authored-by: David Menendez <[email protected]>
  • Loading branch information
AlexanderMelox and davidmenendez authored Oct 22, 2024
1 parent b0b3c1c commit 5b94ed8
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 41 deletions.
1 change: 1 addition & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"dragmode",
"editinplace",
"editsidepanel",
"explainability",
"emptystate",
"erroremptystate",
"explainability",
Expand Down
20 changes: 14 additions & 6 deletions packages/ibm-products-styles/src/components/Card/_card.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
// 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

// Standard imports.
@use '@carbon/styles/scss/theme' as *;
@use '@carbon/styles/scss/spacing' as *;
Expand Down Expand Up @@ -148,16 +150,19 @@ $block-class: #{c4p-settings.$pkg-prefix}--card;
right: $spacing-05;
}

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

.#{$block-class}__header-container--has-slug.#{$block-class}__header-container--has-actions {
.#{$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 {
padding-right: $spacing-08;
}

.#{$block-class}__header-container--has-slug.#{$block-class}__header-container--large-tile-or-label {
.#{$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 {
padding-right: $spacing-09;
}

Expand All @@ -170,15 +175,17 @@ $block-class: #{c4p-settings.$pkg-prefix}--card;
pointer-events: none;
}

.#{$block-class}--has-slug {
.#{$block-class}--has-slug,
.#{$block-class}--has-ai-label {
@include utilities.ai-popover-gradient('default', 0, 'layer');

border: 1px solid transparent;
box-shadow: inset 0 -80px 70px -65px $ai-inner-shadow,
0 4px 10px 2px $ai-drop-shadow;
}

.#{$block-class}__clickable.#{$block-class}--has-slug::before {
.#{$block-class}__clickable.#{$block-class}--has-slug::before,
.#{$block-class}__clickable.#{$block-class}--has-ai-label::before {
@include utilities.ai-popover-gradient('hover', 0, 'layer');

position: absolute;
Expand All @@ -193,7 +200,8 @@ $block-class: #{c4p-settings.$pkg-prefix}--card;
transition: opacity $duration-fast-02 motion(standard, productive);
}

.#{$block-class}__clickable.#{$block-class}--has-slug:hover::before {
.#{$block-class}__clickable.#{$block-class}--has-slug:hover::before,
.#{$block-class}__clickable.#{$block-class}--has-ai-label:hover::before {
opacity: 1;
}

Expand Down
15 changes: 14 additions & 1 deletion packages/ibm-products/src/components/Card/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,16 @@ interface CardProp extends PropsWithChildren {
iconDescription?: string;

/**
* **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
*/
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.
*/
aiLabel?: ReactNode | boolean;

status?: 'complete' | 'incomplete';
title?: ReactNode;
titleSize?: 'default' | 'large';
Expand All @@ -97,6 +102,7 @@ export const Card = forwardRef(
// The component props, in alphabetical order (for consistency).
actionIcons = Object.freeze([]),
actionsPlacement = 'bottom',
aiLabel,
metadata = Object.freeze([]),
children,
className,
Expand Down Expand Up @@ -253,6 +259,7 @@ export const Card = forwardRef(
[`${blockClass}__clickable`]: clickable,
[`${blockClass}__media-left`]: mediaPosition === 'left',
[`${blockClass}--has-slug`]: !!slug,
[`${blockClass}--has-ai-label`]: !!aiLabel,
},
className
),
Expand All @@ -277,6 +284,7 @@ export const Card = forwardRef(

const getHeaderProps = () => ({
actions: actionsPlacement === 'top' ? getActions() : '',
aiLabel,
noActionIcons:
getIcons().length > 0 && actionsPlacement === 'top' ? false : true,
actionsPlacement,
Expand Down Expand Up @@ -384,6 +392,10 @@ 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']),
Expand Down Expand Up @@ -440,6 +452,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
*/
slug: PropTypes.oneOfType([PropTypes.node, PropTypes.bool]),

Expand Down
44 changes: 32 additions & 12 deletions packages/ibm-products/src/components/Card/CardHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ const defaults = {
};

interface CardHeaderProps {
actions?: ReactNode[] | ReactNode;
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.
*/
aiLabel?: ReactNode | boolean;
description?: ReactNode;
hasActions?: boolean;
/**
Expand All @@ -42,6 +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
*/
slug?: ReactNode;

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

export const CardHeader = ({
actions,
aiLabel,
noActionIcons,
onPrimaryButtonClick,
onSecondaryButtonClick,
Expand Down Expand Up @@ -84,9 +90,9 @@ export const CardHeader = ({
[`${actionGhostButton}--only`]: noActionIcons,
});

const hollowSlugIcon = (
const hollowAiIcon = (
<svg
className={`${carbonPrefix}--slug ${carbonPrefix}--slug-icon`}
className={`${carbonPrefix}--slug ${carbonPrefix}--slug-icon`} // NOTE: We cannot change this to ai-label until carbon changes their classnames on their end
width="24"
height="24"
viewBox="0 0 24 24"
Expand All @@ -101,15 +107,23 @@ export const CardHeader = ({
</svg>
);

let normalizedSlug: React.ReactElement<any> | null = null;
if (slug) {
if (inClickableCard || typeof slug === 'boolean') {
normalizedSlug = hollowSlugIcon;
let normalizedAiLabel: React.ReactElement<any> | null = null;
if (aiLabel || slug) {
if (
inClickableCard ||
typeof aiLabel === 'boolean' ||
typeof slug === 'boolean'
) {
normalizedAiLabel = hollowAiIcon;
} else {
normalizedSlug = React.cloneElement(slug as React.ReactElement<any>, {
size:
(label && title) || (title && titleSize === 'large') ? 'sm' : 'xs',
});
const element = aiLabel || slug;
normalizedAiLabel = React.cloneElement(
element as React.ReactElement<any>,
{
size:
(label && title) || (title && titleSize === 'large') ? 'sm' : 'xs',
}
);
}
}

Expand All @@ -119,6 +133,7 @@ export const CardHeader = ({
className={cx([
`${headerClass}-container`,
{ [`${headerClass}-container--has-slug`]: !!slug },
{ [`${headerClass}-container--has-ai-label`]: !!aiLabel },
{ [`${headerClass}-container--has-actions`]: !!hasActions },
{
[`${headerClass}-container--large-tile-or-label`]:
Expand Down Expand Up @@ -165,14 +180,18 @@ export const CardHeader = ({
)}
</div>
)}
{normalizedSlug}
{normalizedAiLabel}
</div>
</div>
);
};
/**@ts-ignore */
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.
*/
aiLabel: PropTypes.oneOfType([PropTypes.node, PropTypes.bool]),
description: PropTypes.oneOfType([
PropTypes.string,
PropTypes.object,
Expand Down Expand Up @@ -200,6 +219,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
*/
slug: PropTypes.oneOfType([PropTypes.node, PropTypes.bool]),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,22 +44,16 @@ const DocsPage = () => (
story: stories.WithSecondaryAction,
},
{
title: 'With Slug',
title: 'With AI Label',
description:
'A Carbon AI slug can be used within the ExpressiveCard using the Slug property.',
'An AI Label is intended for any scenario where something is being generated by AI to reinforce AI transparency, accountability, and explainability at the UI level.',
source: {
language: 'html',
code: `
<ExpressiveCard
label="Label"
primaryButtonText="Primary"
slug={
<Slug className="slug-container" size="xs">
<SlugContent>
Slug content goes here...
</SlugContent>
</Slug>
}
aiLabel={<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 @@ -70,7 +64,7 @@ const DocsPage = () => (
},
{
description:
'Clickable tiles only accept a boolean value of true for the Slug property and display a hollow slug.',
'Clickable tiles only accept a boolean value of true for the aiLabel property.',
source: {
language: 'html',
code: `
Expand All @@ -79,7 +73,7 @@ const DocsPage = () => (
primaryButtonText="Primary"
onClick={() => {}}
onKeyDown={() => {}}
slug={true}
aiLabel={true}
title="Title">
<p>
expressive card body content block. description inviting the user to take action on the card.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,17 @@ export default {
},
options: [0, 1, 2],
},
aiLabel: {
control: {
type: 'select',
labels: {
0: 'No AI label',
1: 'with AI label',
},
default: 0,
},
options: [false, true],
},
},
decorators: [
(Story) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ export interface ExpressiveCardProps extends PropsWithChildren {
* Icons that are displayed on card. Refer to design documentation for implementation guidelines. Note- href will supersede onClick
*/
actionIcons?: ActionIcon[];
/**
* 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?: ReactNode | boolean;
/**
* Content that shows in the body of the card
*/
Expand Down Expand Up @@ -103,6 +107,7 @@ export interface ExpressiveCardProps extends PropsWithChildren {
/**
* **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
*/
slug?: ReactNode | boolean;

Expand Down Expand Up @@ -147,6 +152,10 @@ ExpressiveCard.propTypes = {
href: PropTypes.string,
})
),
/**
* 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]),
/**
* Content that shows in the body of the card
*/
Expand Down Expand Up @@ -233,6 +242,7 @@ ExpressiveCard.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
*/
slug: PropTypes.oneOfType([PropTypes.node, PropTypes.bool]),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,23 +44,17 @@ const DocsPage = () => (
story: stories.WithOverflow,
},
{
title: 'With Slug',
title: 'With AI label',
description:
'A Carbon AI slug can be used within the ProductiveCard using the Slug property.',
'An AI Label is intended for any scenario where something is being generated by AI to reinforce AI transparency, accountability, and explainability at the UI level.',
source: {
language: 'html',
code: `
<ProductiveCard
onClick={() => {}}
onKeyDown={() => {}}
primaryButtonText="Ghost button"
slug={
<Slug className="slug-container" size="xs">
<SlugContent>
Slug content goes here...
</SlugContent>
</Slug>
}
aiLabel={<AILabel>...</AILabel> || true}
title="Title"
>
<React.Fragment key=".0">
Expand All @@ -78,15 +72,15 @@ const DocsPage = () => (
},
{
description:
'Clickable tiles only accept a boolean value of true for the Slug property and display a hollow slug.',
'Clickable tiles only accept a boolean value of true for the aiLabel property.',
source: {
language: 'html',
code: `
<ProductiveCard
onClick={() => {}}
onKeyDown={() => {}}
primaryButtonText="Ghost button"
slug={true}
aiLabel={true}
title="Title"
>
<React.Fragment key=".0">
Expand Down
Loading

0 comments on commit 5b94ed8

Please sign in to comment.