Skip to content

feat(f36-ai-components): add f36-ai-components package#3202

Open
Elliot Massen (elliotmassen) wants to merge 199 commits intomainfrom
PIC-694-forma36-create-aichatlayout-component
Open

feat(f36-ai-components): add f36-ai-components package#3202
Elliot Massen (elliotmassen) wants to merge 199 commits intomainfrom
PIC-694-forma36-create-aichatlayout-component

Conversation

@elliotmassen
Copy link
Copy Markdown
Member

@elliotmassen Elliot Massen (elliotmassen) commented Oct 20, 2025

Summary

  • Introduces the @contentful/f36-ai-components package — a collection of components for building AI conversational interfaces (chat layouts, message rendering, input with mentions, reasoning indicators, side panels, and more)
  • Merges FormaV6 changes from main (React 19, Emotion v11 migration) into the ai-components branch
  • Migrates all 28 files from emotion v10 to @emotion/css v11
  • Bumps all @contentful/f36-* dependencies to ^6.0.0 and updates React peer deps to ^18.3.0 || >=19.1.0
  • Fixes React 19 type compatibility (blockquote HTMLQuoteElement typing, Editor type import from @tiptap/core, icon variant prop removal, controlled Menu requiring onClose)
  • Upgrades react-markdown v7 → v9, rehype-raw v6 → v7, remark-gfm v3 → v4 for React 19 JSX namespace compatibility
  • Updates Storybook 9 imports (@storybook/addon-actionsstorybook/actions, @storybook/react@storybook/react-vite)
  • Renames internal _Component functions to ComponentBase suffix to satisfy react-hooks/rules-of-hooks linter
  • Removes alpha publishing workflow and integrates package into the standard changeset release flow (added to fixed version group)

New Components

  • AIChatLayout — full-page chat layout with side panel support
  • AIChatMessage — markdown-rendered user/assistant messages with action buttons
  • AIChatMessageList — auto-scrolling message list with streaming support
  • AIChatInput — TipTap-based rich text input with @mention support
  • AIChatHistory — tabbed thread history with search
  • AIChatReasoning — expandable reasoning/thinking indicator with animation
  • AIChatSidePanel — collapsible side panel
  • AIChatSuggestionPill — clickable suggestion chips
  • AIChatConversation — conversation container
  • AIChatArtifactMessage — artifact display in messages

Test plan

Chromatic deployment url: https://5fd1dda724cc620021ace8c5-shjeybujej.chromatic.com/?path=/docs/components-aichat-aichatartifactmessage--documentation

  • Build passes (packages/core and packages/f36-ai-components)
  • CI tests and lint pass
  • Storybook visual verification (animations, markdown, input, layout, history)
  • No changes outside f36-ai-components vs main (except package-lock.json)
  • Verify changeset publishes correctly on merge

PR Checklist

  • I have read the relevant readme.md file(s)
  • All commits follow our Git commit message convention
  • Tests are added/updated/not required
  • Tests are passing
  • Storybook stories are added/updated/not required
  • Usage notes are added/updated/not required
  • Has been tested based on Contentful's browser support
  • Doesn't contain any sensitive information

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Oct 20, 2025

⚠️ No Changeset found

Latest commit: ba80189

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link
Copy Markdown

vercel Bot commented Oct 20, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
forma-36 Ready Ready Preview Apr 22, 2026 0:34am
forma-36-storybook Ready Ready Preview Apr 22, 2026 0:34am

Request Review

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Oct 20, 2025

size-limit report 📦

Path Size Loading time (3g) Running time (snapdragon) Total time
CommonJS 132.12 KB (+0.13% 🔺) 2.7 s (+0.13% 🔺) 21 ms (-11.58% 🔽) 2.7 s
Module 128.97 KB (+0.13% 🔺) 2.6 s (+0.13% 🔺) 122 ms (+466.67% 🔺) 2.8 s

Comment thread scripts/changesets/changelog-generate.js Outdated
@@ -0,0 +1,36 @@
{
"name": "@contentful/f36-ai-components",
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Once this PR is approved, should I be running the scripts/prerelease.mjs script to publish this package?

'@contentful/f36-i18n-utils',
'@contentful/f36-website',
'@contentful/f36-docs-utils',
'@contentful/f36-ai-components',
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Is this correct? If so, I have added a note to the prerelease section in RELEASES.md to document it.

Copy link
Copy Markdown
Collaborator

@cf-remylenoir Rémy Lenoir (cf-remylenoir) left a comment

Choose a reason for hiding this comment

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

Blocking until @contentful/team-ui-foundation review and v6 is released

Copy link
Copy Markdown
Collaborator

@mshaaban0 Moe Shaaban (mshaaban0) 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 to me 👍🏼

Comment thread packages/f36-ai-components/src/AIChatLayout/AIChatLayout.styles.ts Outdated
- Added @tiptap/core dependency to package.json.
- Updated AIChatHistoryEmptyState to use tokens for icon color.
- Refactored AIChatInput to import Editor type correctly.
- Modified AIChatMessage to use any type for blockquote props.
- Changed import paths for action from
 '@storybook/addon-actions' to 'storybook/actions' in multiple story files.
…[PIC-949]

- using @storybook/react-vite instead of @storybook/react
- Updated react-markdown to version 9.0.3, rehype-raw to version 7.0.0,
and remark-gfm to version 4.0.0 in package.json.
- Modified AIChatMentionList component to include
onClose handler for the Menu component.
- Changed Storybook import type from '@storybook/react/types-6-0' to '@storybook/react-vite'
in AIChatArtifactMessage stories.
Copy link
Copy Markdown
Collaborator

@mshaaban0 Moe Shaaban (mshaaban0) left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Copy Markdown
Collaborator

@Lelith Kathrin Holzmann (Lelith) left a comment

Choose a reason for hiding this comment

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

I did not have much time for reviewing, but I see some common themes:

Blocking issues:

  • We need to enable all components for internationalization - therefore no hardcoded strings are allowed
  • Tests for components have to include an axe acessibility test

General Improvement

  • We are using the Boxcomponent quite a lot where other components like Flexor Stack should have been preferred

lineHeight: tokens.lineHeightL,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
listStyle: (children as any).type === List ? 'none' : '',
listStyle: (children as any)?.type === List ? 'none' : '',
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I know it's a tiny change, but I would prefer it if we keep the scope specific to AI components, so I would strongly prefer if this change could be applied in its own PR.

import React from 'react';
import { AIChatArtifactMessage } from './AIChatArtifactMessage';

describe('AIChatArtifactMessage', function () {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

:accessibility: Please add an axe test for this component

Comment on lines +47 to +50
<Box className={styles.header}>
<Box className={styles.icon}>{icon}</Box>
<Box className={styles.title}>{title}</Box>
</Box>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

💅 This could be improved by using Flex instead of Box component and safe us some extra lines of css which are changing the default behavior of the div element that is behind the Box component

children: ReactNode;
}

export const AIChatConversation: React.FC<AIChatConversationProps> = ({
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

💅 when i look at what this component delivers and the styles it applies, it could also be replaced entirely with the Flex component

export const AIChatSuggestionList = ({
suggestions,
onSelect,
testId,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

💅 we usually provide a default test id in these cases, to ensure for better component identification

import { MessageGroups } from '../AIChatHistory';
import { AIChatHistoryTabs } from './AIChatHistoryTabs';

describe('AIChatHistoryTabs', () => {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

:accessibility: misses an axe test

Comment on lines +27 to +28
background: 'none',
border: 'none',
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

❓ Why do we need to overwrite defaults?

const threadElement = screen.getByTestId(
'cf-ui-ai-chat-history-thread-test-thread',
);
fireEvent.click(threadElement);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

:accessibility: we prefer userEvent over fireEvent for better accessibility testing

import { MessageThread } from '../AIChatHistory';
import { AIChatHistoryThread } from './AIChatHistoryThread';

describe('AIChatHistoryThread', () => {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

:accessibility: misses axe test

className={styles.threadStatus}
aria-label={`Status: ${thread.status}`}
title="Status"
>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🇩🇪 🇫🇷 These texts need to be translated and therefore should be provided as props or already be offered as translated component using lingui

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Forma 36 isn't set up with Lingui yet, so these texts can't be localized. They can already be exposed via props, like other components.

* chore: tiptap v3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.