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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 63 additions & 4 deletions packages/ui-heading/src/Heading/v2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,78 @@ type: example

### AI Heading

Pre-configured and with unique styles, the `ai-headings` are used for standardized, ai-related components.
Use the `aiVariant` prop for headings of AI-related content, such as a Modal or Popover header. Use a standard Heading for all other headings.

`stacked` and `horizontal` show the IgniteAI logo lockup before the heading text:

```js
---
type: example
---
<div style={{display: 'flex', flexDirection: 'column', gap: '24px'}}>
<Heading aiVariant="stacked" level="h2">Nutrition Facts</Heading>
<Heading aiVariant="horizontal" level="h3">Nutrition Facts</Heading>
<Heading aiVariant="iconOnly" level="h4">Nutrition Facts</Heading>
<Heading aiVariant="stacked" level="h2">Stacked AI heading</Heading>
<Heading aiVariant="horizontal" level="h3">Horizontal AI heading</Heading>
</div>
```

`iconOnly` shows only the AI icon, and it comes in the same sizes as the standard Heading. Set the size with the `variant` or `level` prop, and the icon scales to match:

```js
---
type: example
---
<div style={{display: 'flex', flexDirection: 'column', gap: '16px'}}>
<Heading aiVariant="iconOnly" variant="titlePageDesktop">titlePageDesktop</Heading>
<Heading aiVariant="iconOnly" variant="titlePageMobile">titlePageMobile</Heading>
<Heading aiVariant="iconOnly" variant="titleSection">titleSection</Heading>
<Heading aiVariant="iconOnly" variant="titleCardSection">titleCardSection</Heading>
<Heading aiVariant="iconOnly" variant="titleModule">titleModule</Heading>
<Heading aiVariant="iconOnly" variant="titleCardLarge">titleCardLarge</Heading>
<Heading aiVariant="iconOnly" variant="titleCardRegular">titleCardRegular</Heading>
<Heading aiVariant="iconOnly" variant="titleCardMini">titleCardMini</Heading>
<Heading aiVariant="iconOnly" variant="label">label</Heading>
</div>
```

For example, in a Modal header:

```js
---
type: example
---
const Example = () => {
const [open, setOpen] = useState(false)
return (
<div>
<Button onClick={() => setOpen(true)}>Open Modal</Button>
<Modal
open={open}
onDismiss={() => setOpen(false)}
size="small"
label="Summarize with AI"
>
<Modal.Header>
<CloseButton
placement="end"
offset="small"
onClick={() => setOpen(false)}
screenReaderLabel="Close"
/>
<Heading aiVariant="iconOnly" variant="titleCardLarge">
Summarize with AI
</Heading>
</Modal.Header>
<Modal.Body>
<Text>The AI-generated summary goes here.</Text>
</Modal.Body>
</Modal>
</div>
)
}

render(<Example />)
```

### Heading level

What DOM element is output is determined in the following order:
Expand Down
30 changes: 27 additions & 3 deletions packages/ui-heading/src/Heading/v2/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,22 @@ const variantToAIHorizontalIconSize: Record<
labelInline: 'xs'
}

const variantToAIIconOnlySize: Record<
NonNullable<HeadingProps['variant']>,
'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
> = {
titlePageDesktop: 'xl',
titlePageMobile: 'xl',
titleSection: 'xl',
titleCardSection: 'lg',
titleModule: 'lg',
titleCardLarge: 'lg',
titleCardRegular: 'md',
titleCardMini: 'sm',
label: 'sm',
labelInline: 'sm'
}

const levelToAIHorizontalIconSize: Record<
Exclude<HeadingProps['level'], 'reset' | undefined>,
'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
Expand Down Expand Up @@ -159,10 +175,12 @@ class Heading extends Component<HeadingProps> {
this.checkProps()
}

getIconSize(forAIHorizontal = false) {
getIconSize(aiMode?: 'horizontal' | 'iconOnly') {
const { variant, level, as } = this.props
const forAIHorizontal = !!aiMode

if (variant) {
if (aiMode === 'iconOnly') return variantToAIIconOnlySize[variant]
return forAIHorizontal
? variantToAIHorizontalIconSize[variant]
: variantToIconSize[variant]
Expand Down Expand Up @@ -211,7 +229,10 @@ class Heading extends Component<HeadingProps> {
return (
<span css={this.props.styles?.withIcon} aria-hidden="true">
<span css={this.props.styles?.igniteAIHorizontal}>
<IgniteaiLogoInstUIIcon color="ai" size={this.getIconSize(true)} />
<IgniteaiLogoInstUIIcon
color="ai"
size={this.getIconSize('horizontal')}
/>
<span css={this.props.styles?.igniteAI}>IgniteAI</span>
</span>
{children}
Expand All @@ -221,7 +242,10 @@ class Heading extends Component<HeadingProps> {
if (aiVariant === 'iconOnly') {
return (
<span css={this.props.styles?.withIcon} aria-hidden="true">
<IgniteaiLogoInstUIIcon color="ai" size={this.getIconSize(true)} />
<IgniteaiLogoInstUIIcon
color="ai"
size={this.getIconSize('iconOnly')}
/>
{children}
</span>
)
Expand Down
22 changes: 22 additions & 0 deletions regression-test/src/app/heading/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,28 @@ export default function HeadingPage() {
</Heading>
</section>

{/* AI Heading iconOnly sizes */}
<section style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
{(
[
'titlePageDesktop',
'titlePageMobile',
'titleSection',
'titleCardSection',
'titleModule',
'titleCardLarge',
'titleCardRegular',
'titleCardMini',
'label',
'labelInline'
] as const
).map((variant) => (
<Heading key={variant} aiVariant="iconOnly" variant={variant}>
AI {variant}
</Heading>
))}
</section>

{/* Colors */}
<section>
<Heading>I inherit my color via the CSS cascade (default)</Heading>
Expand Down
Loading