diff --git a/README.md b/README.md index 6405c22e1e..3c02d07e73 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ For components that implement significant logic, it's helpful to split the compo ### Customizing Styles -The preferred method for overriding the pre-defined styles in the library is to two step process. First, import our bundled CSS into the file where you instantiate your chat application. Second, locate any Stream styles you want to override using either the browser inspector or by viewing the library code. You can then add selectors to your local CSS file to override our defaults. For example: +The preferred method for overriding the pre-defined styles in the library is to two-step process. First, import our bundled CSS into the file where you instantiate your chat application. Second, locate any Stream styles you want to override using either the browser inspector or by viewing the library code. You can then add selectors to your local CSS file to override our defaults. For example: ```js import 'stream-chat-react/dist/css/v2/index.css'; diff --git a/docusaurus/docs/React/basics/getting-started.mdx b/docusaurus/docs/React/basics/getting-started.mdx index fa1602fa09..046d29cc1b 100644 --- a/docusaurus/docs/React/basics/getting-started.mdx +++ b/docusaurus/docs/React/basics/getting-started.mdx @@ -76,22 +76,37 @@ body, display: flex; height: 100%; - .str-chat-channel-list { + .str-chat__channel-list { position: fixed; z-index: 1; + height: 100%; width: 0; + flex-shrink: 0; + box-shadow: 0 0 8px rgba(0, 0, 0, 0.15); &--open { - width: 100%; + width: 30%; + position: fixed; } + transition: width 0.3s ease-out; + } + + .str-chat__channel { + flex: 1; + min-width: 0; } - .str-chat-channel { - width: 100%; + .str-chat__main-panel { + min-width: 0; + flex: 1; + + &--thread-open { + display: none; + } } .str-chat__thread { - width: 100%; + flex: 1; height: 100%; position: fixed; z-index: 1; @@ -117,9 +132,8 @@ body, } @media screen and (min-width: 768px) { - .str-chat-channel-list { + .str-chat__channel-list { width: 30%; - max-width: 420px; position: initial; z-index: 0; } @@ -128,11 +142,24 @@ body, position: initial; z-index: 0; } + + .str-chat__channel-header .str-chat__header-hamburger { + display: none; + } } @media screen and (min-width: 1024px) { + .str-chat__main-panel { + min-width: 0; + + &--thread-open { + max-width: 55%; + display: flex; + } + } + .str-chat__thread { - width: 45%; + max-width: 45%; } .str-chat__channel-header .str-chat__header-hamburger { diff --git a/docusaurus/docs/React/components/core-components/thread.mdx b/docusaurus/docs/React/components/core-components/thread.mdx index 7b419f463f..0f14cd15aa 100644 --- a/docusaurus/docs/React/components/core-components/thread.mdx +++ b/docusaurus/docs/React/components/core-components/thread.mdx @@ -169,14 +169,6 @@ Controls injection of `. By assigning this class a CSS rule `display: none` in the default SDK's stylesheet we hid the contents of `Window`. We decided to simplify the logic in this case: + +1. class `str-chat__main-panel--hideOnThread` was replaced by class `str-chat__main-panel--thread-open` +2. the class `str-chat__main-panel--thread-open` is attached to the root `
` always, when thread is open +3. the default value of `hideOnThread` prop was `false` (`Window` contents was not hidden upon opening a thread) and so integrators still have to opt in to hiding the contents upon opening a thread by adding rule `display: none` to `str-chat__main-panel--thread-open` class + +:::important +**Action required** +If your application renders `Window` with `hideOnThread` enabled, and you want to keep this behavior add the following rule to your CSS: + +```css +.str-chat__main-panel--thread-open { + display: none; +} + +.str-chat__main-panel--thread-open + .str-chat__thread { + // occupy the whole space previously occupied by the main message list container + flex: 1; +} +``` + +::: + +### Removal of Thread's fullWidth prop + +Setting the `fullWidth` value to `true` let to assignment of class `str-chat__thread--full` to the `Thread` component's root `
`. This class had support in the SDK's legacy stylesheet only. With the approach of avoiding styling React components via props, the prop has been removed along with the legacy stylesheet. Read more about the the stylesheet removal in the [section **Removal of deprecated components**](#removal-of-deprecated-components). + ## Removal of deprecated components ### Attachment rendering utility functions @@ -330,12 +361,13 @@ Replace the removed classes with their alternatives in the custom CSS. | `QuotedMessagePreview` root `
` | `quoted-message-preview` | no alternative | | `QuotedMessagePreview` | `quoted-message-preview-content` | `str-chat__quoted-message-preview` | | `QuotedMessagePreview` | `quoted-message-preview-content-inner` | `str-chat__quoted-message-bubble` | -| `MessageList` | `str-chat__list--thread` | `str-chat__thread-list` | +| `MessageList` | `str-chat__thread--full` | no alternative | | `InfiniteScroll` rendered by `MessageList` | `str-chat__reverse-infinite-scroll` | `str-chat__message-list-scroll` | | `ScrollToBottomButton` | `str-chat__message-notification-right` | `str-chat__message-notification-scroll-to-latest` | | `ScrollToBottomButton` | `str-chat__message-notification-scroll-to-latest-unread-count` | `str-chat__jump-to-latest-unread-count` | | `ReactionsListModal` | `emoji` | `str-chat__message-reaction-emoji` or `str-chat__message-reaction-emoji--with-fallback` | | `SimpleReactionList` | `str-chat__simple-reactions-list-tooltip` | no alternative - markup removal | +| `Thread` | `str-chat__list--thread` | `str-chat__thread-list` | | `ThreadHeader` | `str-chat__square-button` | `str-chat__close-thread-button` | | `TypingIndicator` | `str-chat__typing-indicator__avatars` | no alternative - markup removal | diff --git a/examples/typescript/src/App.css b/examples/typescript/src/App.css deleted file mode 100644 index ab41e5b2a1..0000000000 --- a/examples/typescript/src/App.css +++ /dev/null @@ -1,18 +0,0 @@ -html, -body { - margin: 0; - padding: 0; - height: 100%; -} - -#root { - height: 100%; -} - -.str-chat-channel-list { - height: 100%; -} - -.str-chat-channel { - height: 100%; -} diff --git a/examples/typescript/src/App.test.tsx b/examples/typescript/src/App.test.tsx deleted file mode 100644 index 4db7ebc25c..0000000000 --- a/examples/typescript/src/App.test.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; -import { render } from '@testing-library/react'; -import App from './App'; - -test('renders learn react link', () => { - const { getByText } = render(); - const linkElement = getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/examples/typescript/src/App.tsx b/examples/typescript/src/App.tsx index 9d9724c908..36a957d6e1 100644 --- a/examples/typescript/src/App.tsx +++ b/examples/typescript/src/App.tsx @@ -1,18 +1,16 @@ import React from 'react'; -import { ChannelFilters, ChannelOptions, ChannelSort, StreamChat, UR } from 'stream-chat'; +import { ChannelFilters, ChannelOptions, ChannelSort, StreamChat } from 'stream-chat'; import { - Chat, Channel, ChannelHeader, ChannelList, - VirtualizedMessageList as MessageList, + Chat, MessageInput, Thread, + VirtualizedMessageList as MessageList, Window, } from 'stream-chat-react'; -import './App.css'; - const params = (new Proxy(new URLSearchParams(window.location.search), { get: (searchParams, property) => searchParams.get(property as string), }) as unknown) as Record; diff --git a/examples/typescript/src/index.scss b/examples/typescript/src/index.scss index 0430ffed46..bd018c06d2 100644 --- a/examples/typescript/src/index.scss +++ b/examples/typescript/src/index.scss @@ -1,5 +1,4 @@ body { - margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; @@ -11,26 +10,56 @@ code { } +body { + margin: 0; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +html, +body, +#root { + margin: unset; + padding: unset; + height: 100%; +} + + #root { display: flex; height: 100%; - .str-chat-channel-list { + .str-chat__channel-list { position: fixed; z-index: 1; + height: 100%; width: 0; + flex-shrink: 0; + box-shadow: 0 0 8px rgba(0, 0, 0, 0.15);; &--open { - width: 100%; + width: 30%; + position: fixed; } + transition: width 0.3s ease-out; } .str-chat__channel { - width: 100%; + flex: 1; + min-width: 0; + } + + .str-chat__main-panel { + min-width: 0; + flex: 1; + + &--thread-open { + display: none; + } } .str-chat__thread { - width: 100%; + flex: 1; height: 100%; position: fixed; z-index: 1; @@ -38,7 +67,9 @@ code { .str-chat__channel-header .str-chat__header-hamburger { width: 30px; - height: 30px; + height: 38px; + padding: var(--xxs-p); + margin-right: var(--xs-m); display: flex; align-items: center; justify-content: center; @@ -46,11 +77,6 @@ code { border: none; background: transparent; - svg { - width: 25px; - height: 25px; - } - &:hover { svg path { fill: var(--primary-color); @@ -58,27 +84,10 @@ code { } } + @media screen and (min-width: 768px) { - //.str-chat-channel-list.thread-open { - // &.menu-open { - // width: 30%; - // height: 100%; - // position: fixed; - // z-index: 1; - // } - // - // &.menu-close { - // width: 0; - // } - // - // & + .channel .menu-button { - // display: block; - // } - //} - - .str-chat-channel-list { + .str-chat__channel-list { width: 30%; - max-width: 420px; position: initial; z-index: 0; } @@ -94,16 +103,17 @@ code { } @media screen and (min-width: 1024px) { - //.str-chat-channel-list { - // max-width: 420px; - // position: initial; - // z-index: 0; - //} + .str-chat__main-panel { + min-width: 0; + + &--thread-open { + max-width: 55%; + display: flex; + } + } .str-chat__thread { - width: 45%; - //position: initial; - //z-index: 0; + max-width: 45%; } .str-chat__channel-header .str-chat__header-hamburger { @@ -111,3 +121,4 @@ code { } } } + diff --git a/examples/vite/src/index.scss b/examples/vite/src/index.scss index 71d3851684..adfdcad277 100644 --- a/examples/vite/src/index.scss +++ b/examples/vite/src/index.scss @@ -20,19 +20,34 @@ body, .str-chat__channel-list { position: fixed; z-index: 1; + height: 100%; width: 0; + flex-shrink: 0; + box-shadow: 0 0 8px rgba(0, 0, 0, 0.15);; &--open { - width: 100%; + width: 30%; + position: fixed; } + transition: width 0.3s ease-out; } .str-chat__channel { - width: 100%; + flex: 1; + min-width: 0; + } + + .str-chat__main-panel { + min-width: 0; + flex: 1; + + &--thread-open { + display: none; + } } .str-chat__thread { - width: 100%; + flex: 1; height: 100%; position: fixed; z-index: 1; @@ -57,27 +72,10 @@ body, } } - @media screen and (min-width: 768px) { - //.str-chat__channel-list.thread-open { - // &.menu-open { - // width: 30%; - // height: 100%; - // position: fixed; - // z-index: 1; - // } - // - // &.menu-close { - // width: 0; - // } - // - // & + .channel .menu-button { - // display: block; - // } - //} + @media screen and (min-width: 768px) { .str-chat__channel-list { width: 30%; - max-width: 420px; position: initial; z-index: 0; } @@ -86,19 +84,24 @@ body, position: initial; z-index: 0; } + + .str-chat__channel-header .str-chat__header-hamburger { + display: none; + } } @media screen and (min-width: 1024px) { - //.str-chat__channel-list { - // max-width: 420px; - // position: initial; - // z-index: 0; - //} + .str-chat__main-panel { + min-width: 0; + + &--thread-open { + max-width: 55%; + display: flex; + } + } .str-chat__thread { - width: 45%; - //position: initial; - //z-index: 0; + max-width: 45%; } .str-chat__channel-header .str-chat__header-hamburger { diff --git a/package.json b/package.json index 9abb7e4b1b..e53ccd30d8 100644 --- a/package.json +++ b/package.json @@ -153,7 +153,7 @@ "@semantic-release/changelog": "^6.0.2", "@semantic-release/git": "^10.0.1", "@stream-io/rollup-plugin-node-builtins": "^2.1.5", - "@stream-io/stream-chat-css": "5.0.0-rc.1", + "@stream-io/stream-chat-css": "5.0.0-rc.2", "@testing-library/jest-dom": "^6.1.4", "@testing-library/react": "^13.1.1", "@testing-library/react-hooks": "^8.0.0", diff --git a/src/components/Thread/Thread.tsx b/src/components/Thread/Thread.tsx index 33d1a6ad85..8970f180c3 100644 --- a/src/components/Thread/Thread.tsx +++ b/src/components/Thread/Thread.tsx @@ -40,8 +40,6 @@ export type ThreadProps< autoFocus?: boolean; /** Injects date separator components into `Thread`, defaults to `false`. To be passed to the underlying `MessageList` or `VirtualizedMessageList` components */ enableDateSeparator?: boolean; - /** Display the thread on 100% width of its parent container. Useful for mobile style view */ - fullWidth?: boolean; /** Custom thread input UI component used to override the default `Input` value stored in `ComponentContext` or the [MessageInputSmall](https://github.com/GetStream/stream-chat-react/blob/master/src/components/MessageInput/MessageInputSmall.tsx) default */ Input?: React.ComponentType; /** Custom thread message UI component used to override the default `Message` value stored in `ComponentContext` */ @@ -82,7 +80,6 @@ const ThreadInner = < additionalVirtualizedMessageListProps, autoFocus = true, enableDateSeparator = false, - fullWidth = false, Input: PropInput, Message: PropMessage, messageActions = Object.keys(MESSAGE_ACTIONS), @@ -128,7 +125,6 @@ const ThreadInner = < const threadClass = customClasses?.thread || clsx('str-chat__thread-container str-chat__thread', { - 'str-chat__thread--full': fullWidth, 'str-chat__thread--virtualized': virtualized, }); diff --git a/src/components/Thread/__tests__/Thread.test.js b/src/components/Thread/__tests__/Thread.test.js index 3acee8f0ce..12306a5766 100644 --- a/src/components/Thread/__tests__/Thread.test.js +++ b/src/components/Thread/__tests__/Thread.test.js @@ -284,16 +284,6 @@ describe('Thread', () => { ); }); - it('should assign the str-chat__thread--full modifier class if the fullWidth prop is set to true', () => { - const { container } = renderComponent({ chatClient, threadProps: { fullWidth: true } }); - expect(container.querySelector('.str-chat__thread--full')).toBeInTheDocument(); - }); - - it('should not assign the str-chat__thread--full modifier class if the fullWidth prop is set to false', () => { - const { container } = renderComponent({ chatClient }); - expect(container.querySelector('.str-chat__thread--full')).not.toBeInTheDocument(); - }); - it('should assign str-chat__thread--virtualized class to the root in virtualized mode', () => { const { container } = renderComponent({ chatClient, diff --git a/src/components/Window/Window.tsx b/src/components/Window/Window.tsx index dedd3680b3..9485eb2c8d 100644 --- a/src/components/Window/Window.tsx +++ b/src/components/Window/Window.tsx @@ -8,9 +8,7 @@ import type { DefaultStreamChatGenerics } from '../../types/types'; export type WindowProps< StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics > = { - /** show or hide the window when a thread is active */ - hideOnThread?: boolean; - /** optional prop to force addition of class str-chat__main-panel--hideOnThread to the Window root element */ + /** optional prop to force addition of class str-chat__main-panel---with-thread-opn to the Window root element */ thread?: StreamMessage; }; @@ -19,14 +17,14 @@ const UnMemoizedWindow = < >( props: PropsWithChildren>, ) => { - const { children, hideOnThread = false, thread: propThread } = props; + const { children, thread: propThread } = props; const { thread: contextThread } = useChannelStateContext('Window'); return (
{children} diff --git a/src/components/Window/__tests__/Window.test.js b/src/components/Window/__tests__/Window.test.js index b15c79d6fd..bf815801ea 100644 --- a/src/components/Window/__tests__/Window.test.js +++ b/src/components/Window/__tests__/Window.test.js @@ -7,64 +7,48 @@ import { Window } from '../Window'; import { ChannelStateProvider } from '../../../context/ChannelStateContext'; import { generateMessage } from '../../../mock-builders'; -const renderComponent = ({ channelStateContextMock, children, props }) => +const renderComponent = ({ channelStateContextMock, props }) => render( - - {children} + + , ); const thread = generateMessage(); -const HIDE_CLASS_NAME = 'str-chat__main-panel--hideOnThread'; +const THREAD_OPEN_CLASS_NAME = 'str-chat__main-panel--thread-open'; describe('Window', () => { - it('should render its children if hideOnThread is false and thread is truthy', () => { - const { getByText } = renderComponent({ + it.each([ + ['add', thread], + ['', undefined], + ])('should %s class str-chat__main-panel--thread-open when thread is open', (_, thread) => { + const { container } = renderComponent({ channelStateContextMock: { thread, }, - children: [
bla
], - props: { hideOnThread: false }, }); - - expect(getByText('bla')).toBeInTheDocument(); + if (thread) { + expect(container.firstChild).toHaveClass(THREAD_OPEN_CLASS_NAME); + } else { + expect(container.firstChild).not.toHaveClass(THREAD_OPEN_CLASS_NAME); + } }); - it('should render its children if hideOnThread is true and thread is falsy', () => { - const { getByText } = renderComponent({ - channelStateContextMock: { - thread: undefined, - }, - children: [
bla
], - props: { hideOnThread: true }, - }); - - expect(getByText('bla')).toBeInTheDocument(); - }); it.each([ - ['not add', 'truthy', 'falsy', 'falsy', true, undefined, undefined], - ['add', 'truthy', 'truthy', 'falsy', true, thread, undefined], - ['add', 'truthy', 'falsy', 'truthy', true, undefined, thread], - ['add', 'truthy', 'truthy', 'truthy', true, thread, thread], - ['not add', 'falsy', 'falsy', 'falsy', false, undefined, undefined], - ['not add', 'falsy', 'truthy', 'falsy', false, thread, undefined], - ['not add', 'falsy', 'falsy', 'truthy', false, undefined, thread], - ['not add', 'falsy', 'truthy', 'truthy', false, thread, thread], + ['add', thread], + ['', undefined], ])( - 'should %s class str-chat__main-panel--hideOnThread if hideOnThread is %s, prop thread is %s, context thread is %s', - (expectation, _, __, ___, hideOnThread, propThread, contextThread) => { + 'should %s class str-chat__main-panel--thread-open when thread is passed via prop', + (_, thread) => { const { container } = renderComponent({ - channelStateContextMock: { - thread: contextThread, - }, - children: [
bla
], - props: { hideOnThread, thread: propThread }, + props: { thread }, }); - if (expectation === 'add') - expect(container.querySelector(`.${HIDE_CLASS_NAME}`)).toBeInTheDocument(); - if (expectation === 'not add') - expect(container.querySelector(`.${HIDE_CLASS_NAME}`)).not.toBeInTheDocument(); + if (thread) { + expect(container.firstChild).toHaveClass(THREAD_OPEN_CLASS_NAME); + } else { + expect(container.firstChild).not.toHaveClass(THREAD_OPEN_CLASS_NAME); + } }, ); }); diff --git a/yarn.lock b/yarn.lock index 94b2794c0e..dbc1d513a8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2526,10 +2526,10 @@ crypto-browserify "^3.11.0" process-es6 "^0.11.2" -"@stream-io/stream-chat-css@5.0.0-rc.1": - version "5.0.0-rc.1" - resolved "https://registry.yarnpkg.com/@stream-io/stream-chat-css/-/stream-chat-css-5.0.0-rc.1.tgz#43cce0b4fc2c3bb155a6b8298117e109568d5944" - integrity sha512-+Az83q1QQMFQevInC53Rnyo9ctbZX+qANxt+JfxANf55CRfY6bvABXOvp1gpHTDfosTPXddz1Ijw1SNPQ7hpdA== +"@stream-io/stream-chat-css@5.0.0-rc.2": + version "5.0.0-rc.2" + resolved "https://registry.yarnpkg.com/@stream-io/stream-chat-css/-/stream-chat-css-5.0.0-rc.2.tgz#05c5195c316f1e0eedf8c2de5b95df96bc6076b5" + integrity sha512-YC21H7o4O8lvpCqhLw1aRnXF2b2GJjkLTjmOtLbhFPKRtkN86wlK6wdVyIhpexTS4+nMlImYL9yCAgxr3hmxSQ== "@stream-io/transliterate@^1.5.5": version "1.5.5"