diff --git a/.changeset/chat-layout.md b/.changeset/chat-layout.md new file mode 100644 index 0000000000..0d93a9f971 --- /dev/null +++ b/.changeset/chat-layout.md @@ -0,0 +1,5 @@ +--- +'@lg-chat/chat-layout': minor +--- + +Initial release of `ChatLayout` diff --git a/README.md b/README.md index a92fe7cc8f..255067d4e1 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,7 @@ import Button from '@leafygreen-ui/button'; | [@lg-charts/legend](./charts/legend) | [![version](https://img.shields.io/npm/v/@lg-charts/legend)](https://www.npmjs.com/package/@lg-charts/legend) | ![downloads](https://img.shields.io/npm/dm/@lg-charts/legend?color=white) | [Live Example](http://mongodb.design/component/legend/live-example) | | [@lg-charts/series-provider](./charts/series-provider) | [![version](https://img.shields.io/npm/v/@lg-charts/series-provider)](https://www.npmjs.com/package/@lg-charts/series-provider) | ![downloads](https://img.shields.io/npm/dm/@lg-charts/series-provider?color=white) | [Live Example](http://mongodb.design/component/series-provider/live-example) | | [@lg-chat/avatar](./chat/avatar) | [![version](https://img.shields.io/npm/v/@lg-chat/avatar)](https://www.npmjs.com/package/@lg-chat/avatar) | ![downloads](https://img.shields.io/npm/dm/@lg-chat/avatar?color=white) | [Live Example](http://mongodb.design/component/avatar/live-example) | +| [@lg-chat/chat-layout](./chat/chat-layout) | [![version](https://img.shields.io/npm/v/@lg-chat/chat-layout)](https://www.npmjs.com/package/@lg-chat/chat-layout) | ![downloads](https://img.shields.io/npm/dm/@lg-chat/chat-layout?color=white) | [Live Example](http://mongodb.design/component/chat-layout/live-example) | | [@lg-chat/chat-window](./chat/chat-window) | [![version](https://img.shields.io/npm/v/@lg-chat/chat-window)](https://www.npmjs.com/package/@lg-chat/chat-window) | ![downloads](https://img.shields.io/npm/dm/@lg-chat/chat-window?color=white) | [Live Example](http://mongodb.design/component/chat-window/live-example) | | [@lg-chat/fixed-chat-window](./chat/fixed-chat-window) | [![version](https://img.shields.io/npm/v/@lg-chat/fixed-chat-window)](https://www.npmjs.com/package/@lg-chat/fixed-chat-window) | ![downloads](https://img.shields.io/npm/dm/@lg-chat/fixed-chat-window?color=white) | [Live Example](http://mongodb.design/component/fixed-chat-window/live-example) | | [@lg-chat/input-bar](./chat/input-bar) | [![version](https://img.shields.io/npm/v/@lg-chat/input-bar)](https://www.npmjs.com/package/@lg-chat/input-bar) | ![downloads](https://img.shields.io/npm/dm/@lg-chat/input-bar?color=white) | [Live Example](http://mongodb.design/component/input-bar/live-example) | diff --git a/chat/chat-layout/README.md b/chat/chat-layout/README.md new file mode 100644 index 0000000000..d1c999a6b7 --- /dev/null +++ b/chat/chat-layout/README.md @@ -0,0 +1,128 @@ +# Chat Layout + +![npm (scoped)](https://img.shields.io/npm/v/@lg-chat/chat-layout.svg) + +#### [View on MongoDB.design](https://www.mongodb.design/component/chat-layout/live-example/) + +## Installation + +### PNPM + +```shell +pnpm add @lg-chat/chat-layout +``` + +### Yarn + +```shell +yarn add @lg-chat/chat-layout +``` + +### NPM + +```shell +npm install @lg-chat/chat-layout +``` + +## Overview + +`@lg-chat/chat-layout` provides a CSS Grid-based layout system for building full-screen chat interfaces with a side nav that can be collapsed or pinned. + +This package exports: + +- `ChatLayout`: The grid container and context provider +- `ChatMain`: Content area component that positions itself in the grid +- `useChatLayoutContext`: Hook for accessing layout state + +## Examples + +### Basic + +```tsx +import { ChatLayout, ChatMain } from '@lg-chat/chat-layout'; + +function MyChatApp() { + return ( + + {/* ChatSideNav will go here */} + +
Your chat content
+
+
+ ); +} +``` + +### With Initial State and Toggle Pinned Callback + +```tsx +import { ChatLayout, ChatMain } from '@lg-chat/chat-layout'; + +function MyChatApp() { + const handleTogglePinned = (isPinned: boolean) => { + console.log('Side nav is now:', isPinned ? 'pinned' : 'collapsed'); + }; + + return ( + + {/* ChatSideNav will go here */} + +
Your chat content
+
+
+ ); +} +``` + +## Properties + +### ChatLayout + +| Prop | Type | Description | Default | +| ------------------------------ | ----------------------------- | --------------------------------------------------------------------------------------------- | ------- | +| `children` | `ReactNode` | The content to render inside the grid layout (`ChatSideNav` and `ChatMain` components) | - | +| `className` _(optional)_ | `string` | Custom CSS class to apply to the grid container | - | +| `initialIsPinned` _(optional)_ | `boolean` | Initial state for whether the side nav is pinned (expanded) | `true` | +| `onTogglePinned` _(optional)_ | `(isPinned: boolean) => void` | Callback fired when the side nav is toggled. Receives the new `isPinned` state as an argument | - | + +All other props are passed through to the underlying `
` element. + +### ChatMain + +| Prop | Type | Description | Default | +| ---------- | ----------- | -------------------------- | ------- | +| `children` | `ReactNode` | The main content to render | - | + +All other props are passed through to the underlying `
` element. + +**Note:** `ChatMain` must be used as a direct child of `ChatLayout` to work correctly within the grid system. + +## Context API + +### useChatLayoutContext + +Hook that returns the current chat layout context: + +```tsx +const { isPinned, togglePin } = useChatLayoutContext(); +``` + +**Returns:** + +| Property | Type | Description | +| ----------- | ------------ | ---------------------------------------- | +| `isPinned` | `boolean` | Whether the side nav is currently pinned | +| `togglePin` | `() => void` | Function to toggle the pinned state | + +## Behavior + +### State Management + +- `ChatLayout` manages the `isPinned` state internally and provides it to all descendants via `ChatLayoutContext` +- When `togglePin` is called: + 1. The `isPinned` state updates + 2. Grid columns resize smoothly via CSS transition + 3. The `onTogglePinned` callback fires (if provided) with the new state value +- Descendant components can consume the context to: + - Read the current `isPinned` state + - Call `togglePin()` to toggle the sidebar diff --git a/chat/chat-layout/package.json b/chat/chat-layout/package.json new file mode 100644 index 0000000000..3d3b26e0c6 --- /dev/null +++ b/chat/chat-layout/package.json @@ -0,0 +1,54 @@ + +{ + "name": "@lg-chat/chat-layout", + "version": "0.0.1", + "description": "LeafyGreen UI Kit Chat Layout", + "main": "./dist/umd/index.js", + "module": "./dist/esm/index.js", + "types": "./dist/types/index.d.ts", + "license": "Apache-2.0", + "exports": { + ".": { + "require": "./dist/umd/index.js", + "import": "./dist/esm/index.js", + "types": "./dist/types/index.d.ts" + }, + "./testing": { + "require": "./dist/umd/testing/index.js", + "import": "./dist/esm/testing/index.js", + "types": "./dist/types/testing/index.d.ts" + } + }, + "scripts": { + "build": "lg-build bundle", + "tsc": "lg-build tsc", + "docs": "lg-build docs" + }, + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@leafygreen-ui/emotion": "workspace:^", + "@leafygreen-ui/lib": "workspace:^", + "@leafygreen-ui/tokens": "workspace:^", + "@lg-tools/test-harnesses": "workspace:^" + }, + "peerDependencies": { + "@lg-chat/leafygreen-chat-provider": "workspace:^" + }, + "devDependencies": { + "@lg-chat/chat-window": "workspace:^", + "@lg-chat/input-bar": "workspace:^", + "@lg-chat/message": "workspace:^", + "@lg-chat/message-feed": "workspace:^", + "@lg-chat/title-bar": "workspace:^" + }, + "homepage": "https://github.com/mongodb/leafygreen-ui/tree/main/chat/chat-layout", + "repository": { + "type": "git", + "url": "https://github.com/mongodb/leafygreen-ui" + }, + "bugs": { + "url": "https://jira.mongodb.org/projects/LG/summary" + } +} diff --git a/chat/chat-layout/src/ChatLayout.stories.tsx b/chat/chat-layout/src/ChatLayout.stories.tsx new file mode 100644 index 0000000000..85108f5011 --- /dev/null +++ b/chat/chat-layout/src/ChatLayout.stories.tsx @@ -0,0 +1,94 @@ +import React from 'react'; +import { ChatWindow } from '@lg-chat/chat-window'; +import { InputBar } from '@lg-chat/input-bar'; +import { + LeafyGreenChatProvider, + Variant, +} from '@lg-chat/leafygreen-chat-provider'; +import { Message } from '@lg-chat/message'; +import { MessageFeed } from '@lg-chat/message-feed'; +import { TitleBar } from '@lg-chat/title-bar'; +import { StoryMetaType } from '@lg-tools/storybook-utils'; +import { StoryFn, StoryObj } from '@storybook/react'; + +import { css } from '@leafygreen-ui/emotion'; + +import { ChatLayout, type ChatLayoutProps, ChatMain } from '.'; + +const meta: StoryMetaType = { + title: 'Composition/Chat/ChatLayout', + component: ChatLayout, + parameters: { + default: 'LiveExample', + }, + decorators: [ + Story => ( +
+ +
+ ), + ], +}; +export default meta; + +const sideNavPlaceholderStyles = css` + background-color: rgba(0, 0, 0, 0.05); + padding: 16px; + min-width: 200px; +`; + +const testMessages = [ + { + id: '1', + messageBody: 'Hello! How can I help you today?', + isSender: false, + }, + { + id: '2', + messageBody: 'I need help with my database query.', + }, + { + id: '3', + messageBody: + 'Sure! I can help with that. What specific issue are you encountering?', + isSender: false, + }, +]; + +const Template: StoryFn = props => ( + + +
ChatSideNav Placeholder
+ + + + + {testMessages.map(msg => ( + + ))} + + {}} /> + + +
+
+); + +export const LiveExample: StoryObj = { + render: Template, + parameters: { + chromatic: { + disableSnapshot: true, + }, + }, +}; diff --git a/chat/chat-layout/src/ChatLayout/ChatLayout.spec.tsx b/chat/chat-layout/src/ChatLayout/ChatLayout.spec.tsx new file mode 100644 index 0000000000..8ef8086019 --- /dev/null +++ b/chat/chat-layout/src/ChatLayout/ChatLayout.spec.tsx @@ -0,0 +1,139 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { ChatLayout, useChatLayoutContext } from '.'; + +describe('packages/chat-layout', () => { + describe('ChatLayout', () => { + test('renders children', () => { + render( + +
Test Content
+
, + ); + expect(screen.getByText('Test Content')).toBeInTheDocument(); + }); + + test('provides isPinned context with default value', () => { + const TestConsumer = () => { + const { isPinned } = useChatLayoutContext(); + return
isPinned: {isPinned.toString()}
; + }; + + render( + + + , + ); + expect(screen.getByText('isPinned: true')).toBeInTheDocument(); + }); + + test('accepts initialIsPinned prop', () => { + const TestConsumer = () => { + const { isPinned } = useChatLayoutContext(); + return
isPinned: {isPinned.toString()}
; + }; + + render( + + + , + ); + expect(screen.getByText('isPinned: false')).toBeInTheDocument(); + }); + + test('togglePin function updates isPinned state', async () => { + const TestConsumer = () => { + const { isPinned, togglePin } = useChatLayoutContext(); + return ( + <> +
isPinned: {isPinned.toString()}
+ + + ); + }; + + render( + + + , + ); + + expect(screen.getByText('isPinned: true')).toBeInTheDocument(); + + await userEvent.click(screen.getByRole('button', { name: 'Toggle' })); + + expect(screen.getByText('isPinned: false')).toBeInTheDocument(); + + await userEvent.click(screen.getByRole('button', { name: 'Toggle' })); + + expect(screen.getByText('isPinned: true')).toBeInTheDocument(); + }); + + test('forwards HTML attributes to the div wrapper', () => { + render( + +
Content
+
, + ); + expect(screen.getByTestId('chat-layout')).toBeInTheDocument(); + }); + + test('calls onTogglePinned callback when togglePin is called', async () => { + const onTogglePinned = jest.fn(); + + const TestConsumer = () => { + const { togglePin } = useChatLayoutContext(); + return ; + }; + + render( + + + , + ); + + const toggleButton = screen.getByRole('button', { name: 'Toggle' }); + + await userEvent.click(toggleButton); + + expect(onTogglePinned).toHaveBeenCalledTimes(1); + expect(onTogglePinned).toHaveBeenCalledWith(false); + + await userEvent.click(toggleButton); + + expect(onTogglePinned).toHaveBeenCalledTimes(2); + expect(onTogglePinned).toHaveBeenCalledWith(true); + }); + + test('onTogglePinned receives correct isPinned value based on initialIsPinned', async () => { + const onTogglePinned = jest.fn(); + + const TestConsumer = () => { + const { togglePin } = useChatLayoutContext(); + return ; + }; + + render( + + + , + ); + + await userEvent.click(screen.getByRole('button', { name: 'Toggle' })); + + expect(onTogglePinned).toHaveBeenCalledWith(true); + }); + + test('applies custom className', () => { + render( + +
Content
+
, + ); + const element = screen.getByTestId('chat-layout'); + expect(element).toHaveClass('custom-class'); + }); + }); +}); diff --git a/chat/chat-layout/src/ChatLayout/ChatLayout.styles.ts b/chat/chat-layout/src/ChatLayout/ChatLayout.styles.ts new file mode 100644 index 0000000000..69168c301a --- /dev/null +++ b/chat/chat-layout/src/ChatLayout/ChatLayout.styles.ts @@ -0,0 +1,27 @@ +import { css, cx } from '@leafygreen-ui/emotion'; +import { transitionDuration } from '@leafygreen-ui/tokens'; + +import { + gridAreas, + SIDE_NAV_WIDTH_COLLAPSED, + SIDE_NAV_WIDTH_PINNED, +} from '../constants'; + +const getBaseContainerStyles = (isPinned: boolean) => css` + display: grid; + grid-template-areas: '${gridAreas.sideNav} ${gridAreas.main}'; + grid-template-columns: ${isPinned + ? `${SIDE_NAV_WIDTH_PINNED}px` + : `${SIDE_NAV_WIDTH_COLLAPSED}px`} 1fr; + height: 100%; + width: 100%; + transition: grid-template-columns ${transitionDuration.default}ms ease-in-out; +`; + +export const getContainerStyles = ({ + className, + isPinned, +}: { + className?: string; + isPinned: boolean; +}) => cx(getBaseContainerStyles(isPinned), className); diff --git a/chat/chat-layout/src/ChatLayout/ChatLayout.tsx b/chat/chat-layout/src/ChatLayout/ChatLayout.tsx new file mode 100644 index 0000000000..73f0386e84 --- /dev/null +++ b/chat/chat-layout/src/ChatLayout/ChatLayout.tsx @@ -0,0 +1,36 @@ +import React, { useCallback, useState } from 'react'; + +import { getContainerStyles } from './ChatLayout.styles'; +import { ChatLayoutProps } from './ChatLayout.types'; +import { ChatLayoutContext } from './ChatLayoutContext'; + +/** + * ChatLayout is a context provider that manages the pinned state of the side nav + * and provides it to all child components. It uses CSS Grid to control the layout + * and positioning the side nav and main content. + */ +export function ChatLayout({ + children, + className, + initialIsPinned = true, + onTogglePinned, + ...rest +}: ChatLayoutProps) { + const [isPinned, setIsPinned] = useState(initialIsPinned); + + const togglePin = useCallback(() => { + const newValue = !isPinned; + setIsPinned(newValue); + onTogglePinned?.(newValue); + }, [isPinned, onTogglePinned]); + + return ( + +
+ {children} +
+
+ ); +} + +ChatLayout.displayName = 'ChatLayout'; diff --git a/chat/chat-layout/src/ChatLayout/ChatLayout.types.ts b/chat/chat-layout/src/ChatLayout/ChatLayout.types.ts new file mode 100644 index 0000000000..f2d86c2bb4 --- /dev/null +++ b/chat/chat-layout/src/ChatLayout/ChatLayout.types.ts @@ -0,0 +1,19 @@ +import { ComponentPropsWithRef } from 'react'; + +import { DarkModeProps } from '@leafygreen-ui/lib'; + +export interface ChatLayoutProps + extends ComponentPropsWithRef<'div'>, + DarkModeProps { + /** + * Initial state for whether the side nav is pinned (expanded). + * @default true + */ + initialIsPinned?: boolean; + + /** + * Callback fired when the side nav is toggled (pinned/unpinned). + * Receives the new `isPinned` state as an argument. + */ + onTogglePinned?: (isPinned: boolean) => void; +} diff --git a/chat/chat-layout/src/ChatLayout/ChatLayoutContext.ts b/chat/chat-layout/src/ChatLayout/ChatLayoutContext.ts new file mode 100644 index 0000000000..5aba25ba09 --- /dev/null +++ b/chat/chat-layout/src/ChatLayout/ChatLayoutContext.ts @@ -0,0 +1,20 @@ +import { createContext, useContext } from 'react'; + +export interface ChatLayoutContextProps { + /** + * Whether the side nav is pinned (expanded) or not + */ + isPinned: boolean; + + /** + * Function to toggle the pinned state of the side nav + */ + togglePin: () => void; +} + +export const ChatLayoutContext = createContext({ + isPinned: true, + togglePin: () => {}, +}); + +export const useChatLayoutContext = () => useContext(ChatLayoutContext); diff --git a/chat/chat-layout/src/ChatLayout/index.ts b/chat/chat-layout/src/ChatLayout/index.ts new file mode 100644 index 0000000000..66fa95a5b4 --- /dev/null +++ b/chat/chat-layout/src/ChatLayout/index.ts @@ -0,0 +1,7 @@ +export { ChatLayout } from './ChatLayout'; +export { type ChatLayoutProps } from './ChatLayout.types'; +export { + ChatLayoutContext, + type ChatLayoutContextProps, + useChatLayoutContext, +} from './ChatLayoutContext'; diff --git a/chat/chat-layout/src/ChatMain/ChatMain.spec.tsx b/chat/chat-layout/src/ChatMain/ChatMain.spec.tsx new file mode 100644 index 0000000000..2f0376ad1e --- /dev/null +++ b/chat/chat-layout/src/ChatMain/ChatMain.spec.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; + +import { ChatLayout } from '../ChatLayout'; + +import { ChatMain } from '.'; + +describe('packages/chat-layout/ChatMain', () => { + describe('ChatMain', () => { + test('renders children', () => { + render( + + +
Main Content
+
+
, + ); + expect(screen.getByText('Main Content')).toBeInTheDocument(); + }); + + test('forwards HTML attributes to the div element', () => { + render( + + + Content + + , + ); + const element = screen.getByTestId('chat-main'); + expect(element).toHaveAttribute('aria-label', 'Chat content'); + }); + + test('forwards ref to the div element', () => { + const ref = React.createRef(); + render( + + Content + , + ); + expect(ref.current).toBeInstanceOf(HTMLDivElement); + expect(ref.current?.tagName).toBe('DIV'); + }); + + test('applies custom className', () => { + render( + + + Content + + , + ); + const element = screen.getByTestId('chat-main'); + expect(element).toHaveClass('custom-class'); + }); + }); +}); diff --git a/chat/chat-layout/src/ChatMain/ChatMain.styles.ts b/chat/chat-layout/src/ChatMain/ChatMain.styles.ts new file mode 100644 index 0000000000..a8279dd2c4 --- /dev/null +++ b/chat/chat-layout/src/ChatMain/ChatMain.styles.ts @@ -0,0 +1,14 @@ +import { css, cx } from '@leafygreen-ui/emotion'; + +import { gridAreas } from '../constants'; + +const baseContainerStyles = css` + grid-area: ${gridAreas.main}; + display: flex; + flex-direction: column; + min-width: 0; + height: 100%; +`; + +export const getContainerStyles = ({ className }: { className?: string }) => + cx(baseContainerStyles, className); diff --git a/chat/chat-layout/src/ChatMain/ChatMain.tsx b/chat/chat-layout/src/ChatMain/ChatMain.tsx new file mode 100644 index 0000000000..7e62d19d72 --- /dev/null +++ b/chat/chat-layout/src/ChatMain/ChatMain.tsx @@ -0,0 +1,21 @@ +import React, { forwardRef } from 'react'; + +import { getContainerStyles } from './ChatMain.styles'; +import { ChatMainProps } from './ChatMain.types'; + +/** + * ChatMain represents the main content area of the chat layout. + * It automatically positions itself in the second column of the parent + * ChatLayout's CSS Grid, allowing the layout to control spacing for the sidebar. + */ +export const ChatMain = forwardRef( + ({ children, className, ...rest }, ref) => { + return ( +
+ {children} +
+ ); + }, +); + +ChatMain.displayName = 'ChatMain'; diff --git a/chat/chat-layout/src/ChatMain/ChatMain.types.ts b/chat/chat-layout/src/ChatMain/ChatMain.types.ts new file mode 100644 index 0000000000..9070c8ea0e --- /dev/null +++ b/chat/chat-layout/src/ChatMain/ChatMain.types.ts @@ -0,0 +1,7 @@ +import { ComponentPropsWithRef } from 'react'; + +import { DarkModeProps } from '@leafygreen-ui/lib'; + +export interface ChatMainProps + extends ComponentPropsWithRef<'div'>, + DarkModeProps {} diff --git a/chat/chat-layout/src/ChatMain/index.ts b/chat/chat-layout/src/ChatMain/index.ts new file mode 100644 index 0000000000..a212ee6fff --- /dev/null +++ b/chat/chat-layout/src/ChatMain/index.ts @@ -0,0 +1,2 @@ +export { ChatMain } from './ChatMain'; +export { type ChatMainProps } from './ChatMain.types'; diff --git a/chat/chat-layout/src/constants.ts b/chat/chat-layout/src/constants.ts new file mode 100644 index 0000000000..7d98611374 --- /dev/null +++ b/chat/chat-layout/src/constants.ts @@ -0,0 +1,7 @@ +export const gridAreas = { + sideNav: 'side-nav', + main: 'main', +} as const; + +export const SIDE_NAV_WIDTH_PINNED = 256; +export const SIDE_NAV_WIDTH_COLLAPSED = 48; diff --git a/chat/chat-layout/src/index.ts b/chat/chat-layout/src/index.ts new file mode 100644 index 0000000000..8daa958273 --- /dev/null +++ b/chat/chat-layout/src/index.ts @@ -0,0 +1,7 @@ +export { + ChatLayout, + type ChatLayoutContextProps, + type ChatLayoutProps, + useChatLayoutContext, +} from './ChatLayout'; +export { ChatMain, type ChatMainProps } from './ChatMain'; diff --git a/chat/chat-layout/tsconfig.json b/chat/chat-layout/tsconfig.json new file mode 100644 index 0000000000..c4586711f5 --- /dev/null +++ b/chat/chat-layout/tsconfig.json @@ -0,0 +1,25 @@ +{ + "extends": "@lg-tools/build/config/package.tsconfig.json", + "compilerOptions": { + "paths": { + "@leafygreen-ui/icon/dist/*": ["../../packages/icon/src/generated/*"], + "@leafygreen-ui/*": ["../../packages/*/src"] + } + }, + "include": ["src/**/*"], + "exclude": ["**/*.stories.*"], + "references": [ + { + "path": "../../packages/emotion" + }, + { + "path": "../../packages/lib" + }, + { + "path": "../../packages/tokens" + }, + { + "path": "../../tools/test-harnesses" + } + ] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index acb0d6c221..b330dd06dc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -338,6 +338,40 @@ importers: specifier: workspace:^ version: link:../../tools/build + chat/chat-layout: + dependencies: + '@leafygreen-ui/emotion': + specifier: workspace:^ + version: link:../../packages/emotion + '@leafygreen-ui/lib': + specifier: workspace:^ + version: link:../../packages/lib + '@leafygreen-ui/tokens': + specifier: workspace:^ + version: link:../../packages/tokens + '@lg-chat/leafygreen-chat-provider': + specifier: workspace:^ + version: link:../leafygreen-chat-provider + '@lg-tools/test-harnesses': + specifier: workspace:^ + version: link:../../tools/test-harnesses + devDependencies: + '@lg-chat/chat-window': + specifier: workspace:^ + version: link:../chat-window + '@lg-chat/input-bar': + specifier: workspace:^ + version: link:../input-bar + '@lg-chat/message': + specifier: workspace:^ + version: link:../message + '@lg-chat/message-feed': + specifier: workspace:^ + version: link:../message-feed + '@lg-chat/title-bar': + specifier: workspace:^ + version: link:../title-bar + chat/chat-window: dependencies: '@leafygreen-ui/emotion': @@ -1612,7 +1646,7 @@ importers: version: link:../icon-button '@leafygreen-ui/leafygreen-provider': specifier: '>=3.2.0' - version: 5.0.4(@babel/core@7.28.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 5.0.4(@babel/core@7.24.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@leafygreen-ui/lib': specifier: workspace:^ version: link:../lib @@ -14185,6 +14219,14 @@ snapshots: '@juggle/resize-observer@3.4.0': {} + '@leafygreen-ui/emotion@5.0.3(@babel/core@7.24.3)': + dependencies: + '@emotion/css': 11.9.0(@babel/core@7.24.3) + '@emotion/server': 11.11.0(@emotion/css@11.9.0(@babel/core@7.24.3)) + transitivePeerDependencies: + - '@babel/core' + - supports-color + '@leafygreen-ui/emotion@5.0.3(@babel/core@7.28.0)': dependencies: '@emotion/css': 11.9.0(@babel/core@7.28.0) @@ -14193,6 +14235,16 @@ snapshots: - '@babel/core' - supports-color + '@leafygreen-ui/hooks@9.2.0(@babel/core@7.24.3)(react@18.3.1)': + dependencies: + '@leafygreen-ui/lib': 15.6.2(react@18.3.1) + '@leafygreen-ui/tokens': 3.2.4(@babel/core@7.24.3)(react@18.3.1) + lodash: 4.17.21 + transitivePeerDependencies: + - '@babel/core' + - react + - supports-color + '@leafygreen-ui/hooks@9.2.0(@babel/core@7.28.0)(react@18.3.1)': dependencies: '@leafygreen-ui/lib': 15.6.2(react@18.3.1) @@ -14203,6 +14255,17 @@ snapshots: - react - supports-color + '@leafygreen-ui/leafygreen-provider@5.0.4(@babel/core@7.24.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@leafygreen-ui/hooks': 9.2.0(@babel/core@7.24.3)(react@18.3.1) + '@leafygreen-ui/lib': 15.6.2(react@18.3.1) + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + transitivePeerDependencies: + - '@babel/core' + - react + - react-dom + - supports-color + '@leafygreen-ui/leafygreen-provider@5.0.4(@babel/core@7.28.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@leafygreen-ui/hooks': 9.2.0(@babel/core@7.28.0)(react@18.3.1) @@ -14221,6 +14284,17 @@ snapshots: '@leafygreen-ui/palette@5.0.2': {} + '@leafygreen-ui/tokens@3.2.4(@babel/core@7.24.3)(react@18.3.1)': + dependencies: + '@leafygreen-ui/emotion': 5.0.3(@babel/core@7.24.3) + '@leafygreen-ui/lib': 15.6.2(react@18.3.1) + '@leafygreen-ui/palette': 5.0.2 + polished: 4.3.1 + transitivePeerDependencies: + - '@babel/core' + - react + - supports-color + '@leafygreen-ui/tokens@3.2.4(@babel/core@7.28.0)(react@18.3.1)': dependencies: '@leafygreen-ui/emotion': 5.0.3(@babel/core@7.28.0) diff --git a/tools/install/src/ALL_PACKAGES.ts b/tools/install/src/ALL_PACKAGES.ts index d0f42be39c..238a0402e6 100644 --- a/tools/install/src/ALL_PACKAGES.ts +++ b/tools/install/src/ALL_PACKAGES.ts @@ -85,6 +85,7 @@ export const ALL_PACKAGES = [ '@lg-charts/legend', '@lg-charts/series-provider', '@lg-chat/avatar', + '@lg-chat/chat-layout', '@lg-chat/chat-window', '@lg-chat/fixed-chat-window', '@lg-chat/input-bar',