From 91d81cd04d3684fbae65fa71717513ed6505d8e3 Mon Sep 17 00:00:00 2001 From: Pavel Angelov Date: Mon, 10 Nov 2025 10:45:33 +0200 Subject: [PATCH 01/12] everything --- .../ui/src/assets/img/pub-sub/light-bulb.svg | 114 +++++++++ .../ui/src/pages/pub-sub/PubSubPage.tsx | 56 ++--- .../EmptyMessagesList.spec.tsx | 59 +++-- .../EmptyMessagesList/EmptyMessagesList.tsx | 89 ++++--- .../EmptyMessagesList/styles.module.scss | 83 ------- .../MessageListWrapper.styles.tsx | 20 ++ .../MessagesListWrapper.spec.tsx | 75 ++++-- .../messages-list/MessagesListWrapper.tsx | 100 ++++++-- .../publish-message/PublishMessage.spec.tsx | 221 +++++++++++++++++- .../publish-message/PublishMessage.styles.tsx | 6 + .../publish-message/PublishMessage.tsx | 132 +++++------ .../publish-message/styles.module.scss | 46 ---- .../subscribe-form/SubscribeForm.spec.tsx | 134 +++++++++++ .../subscribe-form/SubscribeForm.tsx | 69 ++++++ .../components/subscribe-form/index.ts | 3 + .../ui/src/pages/pub-sub/styles.module.scss | 24 -- 16 files changed, 878 insertions(+), 353 deletions(-) create mode 100644 redisinsight/ui/src/assets/img/pub-sub/light-bulb.svg delete mode 100644 redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/styles.module.scss create mode 100644 redisinsight/ui/src/pages/pub-sub/components/messages-list/MessageListWrapper.styles.tsx create mode 100644 redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.styles.tsx delete mode 100644 redisinsight/ui/src/pages/pub-sub/components/publish-message/styles.module.scss create mode 100644 redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.spec.tsx create mode 100644 redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.tsx create mode 100644 redisinsight/ui/src/pages/pub-sub/components/subscribe-form/index.ts delete mode 100644 redisinsight/ui/src/pages/pub-sub/styles.module.scss diff --git a/redisinsight/ui/src/assets/img/pub-sub/light-bulb.svg b/redisinsight/ui/src/assets/img/pub-sub/light-bulb.svg new file mode 100644 index 0000000000..7f0a69486b --- /dev/null +++ b/redisinsight/ui/src/assets/img/pub-sub/light-bulb.svg @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/redisinsight/ui/src/pages/pub-sub/PubSubPage.tsx b/redisinsight/ui/src/pages/pub-sub/PubSubPage.tsx index 6809cdc9fd..3034923f5a 100644 --- a/redisinsight/ui/src/pages/pub-sub/PubSubPage.tsx +++ b/redisinsight/ui/src/pages/pub-sub/PubSubPage.tsx @@ -15,33 +15,14 @@ import { OnboardingTour } from 'uiSrc/components' import { ONBOARDING_FEATURES } from 'uiSrc/components/onboarding-features' import { incrementOnboardStepAction } from 'uiSrc/slices/app/features' import { OnboardingSteps } from 'uiSrc/constants/onboarding' -import { - MessagesListWrapper, - PublishMessage, - SubscriptionPanel, -} from './components' - -import styles from './styles.module.scss' +import { MessagesListWrapper, PublishMessage } from './components' -// Styled components -const MainContainer = styled.div>` - border: 1px solid ${({ theme }) => theme.semantic.color.border.neutral500}; - border-radius: 8px; -` - -const ContentPanel = styled.div` - flex-grow: 1; -` +import { Col, FlexItem } from 'uiSrc/components/base/layout/flex' +import { Theme } from 'uiSrc/components/base/theme/types' -const HeaderPanel = styled.div` - padding: 12px 18px; - border-bottom: 1px solid var(--separatorColor); - border-color: ${({ theme }) => theme.semantic.color.border.neutral500}; -` - -const FooterPanel = styled.div` - margin-top: 16px; - padding: 10px 18px 28px; +const FooterPanel = styled(FlexItem)` + border-top: 1px solid ${({ theme }) => theme.semantic.color.border.neutral500}; + padding: ${({ theme }: { theme: Theme }) => theme.core.space.space300}; ` const PubSubPage = () => { @@ -92,28 +73,25 @@ const PubSubPage = () => { } return ( - - - - - -
- -
-
- + + + + + + -
+ + {/* TODO: Get rid of hardcoded values */} + -
-
+ + ) } diff --git a/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.spec.tsx b/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.spec.tsx index 36b018a561..2aea895bf8 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.spec.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.spec.tsx @@ -1,44 +1,71 @@ import React from 'react' import { ConnectionType } from 'uiSrc/slices/interfaces' -import { render } from 'uiSrc/utils/test-utils' +import { render, screen } from 'uiSrc/utils/test-utils' import EmptyMessagesList from './EmptyMessagesList' describe('EmptyMessagesList', () => { - it('should render', () => { - expect(render()).toBeTruthy() + it('renders base layout and copy', () => { + render() + + expect(screen.getByTestId('empty-messages-list')).toBeInTheDocument() + + expect(screen.getByText('You are not subscribed')).toBeInTheDocument() + expect( + screen.getByText( + /Subscribe to the Channel to see all the messages published to your database/i, + ), + ).toBeInTheDocument() + + expect( + screen.getByText( + /Running in production may decrease performance and memory available\./i, + ), + ).toBeInTheDocument() }) - it('should render cluster info for Cluster connection type', () => { - const { queryByTestId } = render( + it('shows cluster banner only when Cluster AND isSpublishNotSupported=true', () => { + // visible when both conditions true + const { rerender } = render( , ) + const banner = screen.getByTestId('empty-messages-list-cluster') + expect(banner).toBeInTheDocument() + expect( + screen.getByText( + /Messages published with SPUBLISH will not appear in this channel/i, + ), + ).toBeInTheDocument() - expect(queryByTestId('empty-messages-list-cluster')).toBeInTheDocument() - }) - - it(' not render cluster info for Cluster connection type', () => { - const { queryByTestId } = render( + // hide when flag is false + rerender( , ) + expect( + screen.queryByTestId('empty-messages-list-cluster'), + ).not.toBeInTheDocument() - expect(queryByTestId('empty-messages-list-cluster')).not.toBeInTheDocument() - }) - - it('should not render cluster info for Cluster connection type', () => { - const { queryByTestId } = render( + // hide when connection is not Cluster + rerender( , ) + expect( + screen.queryByTestId('empty-messages-list-cluster'), + ).not.toBeInTheDocument() - expect(queryByTestId('empty-messages-list-cluster')).not.toBeInTheDocument() + // also hide when connectionType is undefined + rerender() + expect( + screen.queryByTestId('empty-messages-list-cluster'), + ).not.toBeInTheDocument() }) }) diff --git a/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.tsx b/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.tsx index e164e9c1d2..7c342be2da 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.tsx @@ -1,54 +1,85 @@ import React from 'react' -import cx from 'classnames' - +import styled from 'styled-components' import { ConnectionType } from 'uiSrc/slices/interfaces' -import { Text } from 'uiSrc/components/base/text' +import { Text, Title } from 'uiSrc/components/base/text' +import { Col, FlexItem } from 'uiSrc/components/base/layout/flex' +import { Banner, RiImage } from 'uiSrc/components/base/display' +import { Spacer } from 'uiSrc/components/base/layout' +import { CallOut } from 'uiSrc/components/base/display/call-out/CallOut' +import LightBulbImage from 'uiSrc/assets/img/pub-sub/light-bulb.svg' -import { RiIcon } from 'uiSrc/components/base/icons/RiIcon' -import styles from './styles.module.scss' -import { Row } from 'uiSrc/components/base/layout/flex' +import SubscribeForm from '../../subscribe-form' export interface Props { connectionType?: ConnectionType isSpublishNotSupported: boolean } +const InnerContainer = styled(Col)` + background-color: ${({ theme }) => + theme.semantic.color.background.neutral300}; + border-radius: ${({ theme }) => theme.core.space.space100}; + border: 1px solid ${({ theme }) => theme.semantic.color.border.neutral500}; + padding: ${({ theme }) => theme.core.space.space300}; + height: 100%; +` + +const Wrapper = styled(FlexItem)` + margin: ${({ theme }) => theme.core.space.space500}; + height: 100%; +` + const EmptyMessagesList = ({ connectionType, isSpublishNotSupported, }: Props) => ( -
-
+ - No messages to display - + + + + + You are not subscribed + + + + Subscribe to the Channel to see all the messages published to your database - - - - Running in production may decrease performance and memory available - - + + + + + + + + + Running in production may decrease performance and memory available. + + {connectionType === ConnectionType.Cluster && isSpublishNotSupported && ( <> -
- + + - {'Messages published with '} - SPUBLISH - {' will not appear in this channel'} - + variant="attention" + showIcon={true} + message="Messages published with SPUBLISH will not appear in this channel" + /> )} -
-
+ + ) export default EmptyMessagesList diff --git a/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/styles.module.scss b/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/styles.module.scss deleted file mode 100644 index ee6362dc5d..0000000000 --- a/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/styles.module.scss +++ /dev/null @@ -1,83 +0,0 @@ -.container { - @include eui.scrollBar; - display: flex; - justify-content: center; - overflow: auto; - - width: 100%; - height: 100%; -} - -.content { - display: flex; - flex-direction: column; - align-items: center; - text-align: center; - margin: auto; - padding: 0 20px; - - width: auto; - height: 110px; - - &Cluster { - height: 184px; - } - - .title { - font-size: 16px !important; - line-height: 24px !important; - letter-spacing: 0px !important; - padding-bottom: 24px; - } - - .summary { - font-size: 13px !important; - line-height: 18px !important; - letter-spacing: -0.13px !important; - color: var(--euiColorMediumShade) !important; - padding-bottom: 18px; - } - - .alert { - font-size: 13px !important; - line-height: 18px !important; - letter-spacing: -0.13px !important; - color: var(--euiColorWarningLight) !important; - } - - .cluster { - font-size: 13px !important; - line-height: 18px !important; - letter-spacing: -0.13px !important; - color: var(--textColorShade) !important; - } - - .alertIcon { - margin-right: 6px; - margin-top: -3px; - } - - .badge { - font-size: 12px; - font-weight: 500; - line-height: 18px; - padding: 0 8px; - display: inline-block; - text-decoration: none; - border-radius: 4px; - white-space: nowrap; - vertical-align: middle; - cursor: default; - max-width: 100%; - text-align: left; - color: var(--htmlColor); - background-color: var(--separatorColor); - } - - .separator { - height: 0px; - width: 192px; - border: 1px solid var(--separatorColor); - margin: 30px 0; - } -} diff --git a/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessageListWrapper.styles.tsx b/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessageListWrapper.styles.tsx new file mode 100644 index 0000000000..4981fbae37 --- /dev/null +++ b/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessageListWrapper.styles.tsx @@ -0,0 +1,20 @@ +import styled from 'styled-components' +import { Col, FlexItem } from 'uiSrc/components/base/layout/flex' + +export const InnerContainer = styled(FlexItem)` + background-color: ${({ theme }) => + theme.semantic.color.background.neutral300}; + border-radius: ${({ theme }) => theme.core.space.space100}; + border: 1px solid ${({ theme }) => theme.semantic.color.border.neutral500}; + padding: ${({ theme }) => theme.core.space.space300}; + margin: ${({ theme }) => theme.core.space.space200}; +` + +export const Wrapper = styled(Col)` + margin: ${({ theme }) => theme.core.space.space200}; + /* + TODO: Remove margin-top when + don't apply custom padding to the page + */ + margin-top: ${({ theme }) => theme.core.space.space100}; +` diff --git a/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessagesListWrapper.spec.tsx b/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessagesListWrapper.spec.tsx index e2109e1688..17033a679b 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessagesListWrapper.spec.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessagesListWrapper.spec.tsx @@ -1,7 +1,7 @@ import React from 'react' -import { render } from 'uiSrc/utils/test-utils' +import { render, screen } from 'uiSrc/utils/test-utils' -import { pubSubSelector } from 'uiSrc/slices/pubsub/pubsub' +import { pubSubSelector as pubSubSelectorMock } from 'uiSrc/slices/pubsub/pubsub' import MessagesListWrapper from './MessagesListWrapper' jest.mock('uiSrc/slices/pubsub/pubsub', () => ({ @@ -12,11 +12,13 @@ jest.mock('uiSrc/slices/pubsub/pubsub', () => ({ }), })) -describe('MessagesListWrapper', () => { - it('should render', () => { - expect(render()).toBeTruthy() - }) +const pubSubSelector = pubSubSelectorMock as jest.Mock + +afterEach(() => { + jest.clearAllMocks() +}) +describe('MessagesListWrapper', () => { it('should render EmptyMessagesList by default', () => { const { queryByTestId } = render() @@ -24,25 +26,72 @@ describe('MessagesListWrapper', () => { expect(queryByTestId('empty-messages-list')).toBeInTheDocument() }) - it('should render MessagesList if isSubscribed === true', () => { - ;(pubSubSelector as jest.Mock).mockReturnValue({ + it('should render empty MessagesList if client is subscribed and no messages', () => { + pubSubSelector.mockReturnValue({ isSubscribed: true, + messages: [], }) const { queryByTestId } = render() expect(queryByTestId('messages-list')).toBeInTheDocument() expect(queryByTestId('empty-messages-list')).not.toBeInTheDocument() + expect(screen.queryByText('No messages published yet')).toBeInTheDocument() }) - it('should render MessagesList if messages.length !== 0', () => { - ;(pubSubSelector as jest.Mock).mockReturnValue({ + it('should render messages if there are some', () => { + pubSubSelector.mockReturnValue({ messages: [{ time: 123, channel: 'channel', message: 'msg' }], }) - const { queryByTestId } = render() + render() - expect(queryByTestId('messages-list')).toBeInTheDocument() - expect(queryByTestId('empty-messages-list')).not.toBeInTheDocument() + expect(screen.queryByTestId('messages-list')).toBeInTheDocument() + expect(screen.queryByTestId('empty-messages-list')).not.toBeInTheDocument() + expect(screen.queryByText('msg')).toBeInTheDocument() + }) + + it('should render messages if there are some no matter if client is subscribed', () => { + pubSubSelector.mockReturnValue({ + messages: [{ time: 123, channel: 'channel', message: 'msg' }], + isSubscribed: false, + }) + + render() + expect(screen.queryByText('msg')).toBeInTheDocument() + }) + + it('should render header with count and "Subscribed" badge when subscribed and no messages', () => { + pubSubSelector.mockReturnValue({ isSubscribed: true, messages: [] }) + + render() + + expect(screen.getByText('Messages:')).toBeInTheDocument() + expect(screen.getByText('0')).toBeInTheDocument() + expect(screen.getByText('Status:')).toBeInTheDocument() + expect(screen.getByText('Subscribed')).toBeInTheDocument() + + expect(screen.getByText('Timestamp')).toBeInTheDocument() + expect(screen.getByText('Channel')).toBeInTheDocument() + expect(screen.getByText('Message')).toBeInTheDocument() + + expect(screen.getByText('No messages published yet')).toBeInTheDocument() + }) + + it('should render header with count and "Unsubscribed" badge when messages exist but not subscribed', () => { + const items = [ + { time: 123, channel: 'a', message: 'x' }, + { time: 456, channel: 'b', message: 'y' }, + ] + pubSubSelector.mockReturnValue({ isSubscribed: false, messages: items }) + + render() + + expect(screen.getByText('Messages:')).toBeInTheDocument() + expect(screen.getByText(String(items.length))).toBeInTheDocument() + expect(screen.getByText('Status:')).toBeInTheDocument() + expect(screen.getByText('Unsubscribed')).toBeInTheDocument() + + expect(screen.getByTestId('messages-list')).toBeInTheDocument() }) }) diff --git a/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessagesListWrapper.tsx b/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessagesListWrapper.tsx index 451b696e88..d703adedd4 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessagesListWrapper.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessagesListWrapper.tsx @@ -10,7 +10,20 @@ import { useConnectionType } from 'uiSrc/components/hooks/useConnectionType' import EmptyMessagesList from './EmptyMessagesList' import MessagesList from './MessagesList' -import styles from './MessagesList/styles.module.scss' +import { Row } from 'uiSrc/components/base/layout/flex' +import { Text } from 'uiSrc/components/base/text' +import { RiBadge } from 'uiSrc/components/base/display/badge/RiBadge' +import { Spacer, HorizontalSpacer } from 'uiSrc/components/base/layout' +import SubscribeForm from '../subscribe-form' +import { InnerContainer, Wrapper } from './MessageListWrapper.styles' + +const SubscribeStatus = ({ isSubscribed }: { isSubscribed: boolean }) => { + if (!isSubscribed) { + return + } + + return +} const MessagesListWrapper = () => { const { messages = [], isSubscribed } = useSelector(pubSubSelector) @@ -29,31 +42,68 @@ const MessagesListWrapper = () => { ) }, [version]) + const hasMessages = messages.length > 0 + + if (hasMessages || isSubscribed) { + return ( + + + + Messages: + + {messages.length} + + + + Status: + + + + + + + + + + + Timestamp + + Channel + + Message + + + + + {hasMessages && ( + <> + + {({ width, height }) => ( + + )} + + + )} + + {!hasMessages && ( + + No messages published yet + + )} + + + ) + } + return ( - <> - {(messages.length > 0 || isSubscribed) && ( -
-
-
Timestamp
-
Channel
-
Message
-
-
- - {({ width, height }) => ( - - )} - -
-
- )} - {messages.length === 0 && !isSubscribed && ( - - )} - + ) } diff --git a/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.spec.tsx b/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.spec.tsx index 7e7f13021b..dedf6610e3 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.spec.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.spec.tsx @@ -1,37 +1,238 @@ -import { fireEvent } from '@testing-library/react' -import { cloneDeep } from 'lodash' import React from 'react' -import { publishMessage } from 'uiSrc/slices/pubsub/pubsub' +import { act, fireEvent, waitFor } from '@testing-library/react' +import { cloneDeep } from 'lodash' + import { cleanup, - clearStoreActions, + initialStateDefault, + mockStore, mockedStore, render, screen, } from 'uiSrc/utils/test-utils' +import { ConnectionType } from 'uiSrc/slices/interfaces' import PublishMessage from './PublishMessage' +import { publishMessageAction } from 'uiSrc/slices/pubsub/pubsub' +import { setPubSubFieldsContext } from 'uiSrc/slices/app/context' + +let mockedConnType = ConnectionType.Standalone +jest.mock('uiSrc/components/hooks/useConnectionType', () => ({ + useConnectionType: () => mockedConnType, +})) + +jest.mock('uiSrc/slices/pubsub/pubsub', () => { + const actual = jest.requireActual('uiSrc/slices/pubsub/pubsub') + return { + ...actual, + publishMessageAction: jest.fn( + (instanceId, channel, message, onSuccess) => (dispatch: any) => { + const action = { + type: 'pubsub/publishMessageAction', + payload: [instanceId, channel, message, onSuccess], + } + dispatch(action) + return Promise.resolve() + }, + ), + } +}) + +jest.mock('uiSrc/slices/app/context', () => { + const actual = jest.requireActual('uiSrc/slices/app/context') + return { + ...actual, + appContextPubSub: (state: any) => + state?.app?.context?.pubsub ?? { channel: '', message: '' }, + setPubSubFieldsContext: jest.fn((fields: any) => (dispatch: any) => { + const action = { + type: 'app/setPubSubFieldsContext', + payload: fields, + } + dispatch(action) + return action + }), + } +}) let store: typeof mockedStore +const createTestStateWithContext = ( + contextOverrides = {}, + pubsubOverrides = {}, +) => { + const state = cloneDeep(initialStateDefault) + state.app.context = { + ...state.app.context, + ...contextOverrides, + } + state.pubsub = { + loading: false, + publishing: false, + error: '', + subscriptions: [], + isSubscribeTriggered: false, + isConnected: false, + isSubscribed: false, + messages: [], + count: 0, + ...pubsubOverrides, + } + return state +} + +const renderPublishMessage = (contextOverrides = {}, pubsubOverrides = {}) => { + const initialStoreState = createTestStateWithContext( + contextOverrides, + pubsubOverrides, + ) + return render(, { + store: mockStore(initialStoreState), + }) +} + +const getChannelField = () => screen.getByTestId('field-channel-name') +const getMessageField = () => screen.getByTestId('field-message') +const getSubmitBtn = () => screen.getByTestId('publish-message-submit') + beforeEach(() => { + jest.useFakeTimers() cleanup() store = cloneDeep(mockedStore) store.clearActions() + jest.mocked(publishMessageAction).mockClear() + jest.mocked(setPubSubFieldsContext).mockClear() + mockedConnType = ConnectionType.Standalone +}) + +afterEach(() => { + jest.runOnlyPendingTimers() + jest.useRealTimers() }) describe('PublishMessage', () => { - it('should render', () => { + it('should render basic form fields and button', () => { expect(render()).toBeTruthy() + expect(getChannelField()).toBeInTheDocument() + expect(getMessageField()).toBeInTheDocument() + expect(getSubmitBtn()).toBeInTheDocument() + }) + + it('should initialize channel/message from app context', async () => { + renderPublishMessage({ + pubsub: { channel: 'orders', message: 'hello' }, + }) + + await waitFor(() => { + expect(screen.getByDisplayValue('orders')).toBeInTheDocument() + expect(screen.getByDisplayValue('hello')).toBeInTheDocument() + }) }) - it('should dispatch subscribe action after submit', () => { + it('should dispatche publish action with instanceId, channel, message, and a callback', () => { + render(, { + store: mockedStore, + }) + + fireEvent.change(getChannelField(), { + target: { value: 'news' }, + }) + fireEvent.change(getMessageField(), { + target: { value: 'ping' }, + }) + + fireEvent.click(getSubmitBtn()) + + const actions = mockedStore.getActions() + expect(actions[0].type).toBe('pubsub/publishMessageAction') + + const [iid, ch, msg, cb] = actions[0].payload + expect(iid).toBe('instanceId') + expect(ch).toBe('news') + expect(msg).toBe('ping') + expect(typeof cb).toBe('function') + }) + + it('should clear message, shows success badge with affected clients, hides button on success published message', async () => { render() - const expectedActions = [publishMessage()] - fireEvent.click(screen.getByTestId('publish-message-submit')) - expect(clearStoreActions(store.getActions())).toEqual( - clearStoreActions(expectedActions), + fireEvent.change(getChannelField(), { + target: { value: 'alpha' }, + }) + fireEvent.change(getMessageField(), { + target: { value: 'hello world' }, + }) + fireEvent.click(getSubmitBtn()) + + const [, , , onSuccess] = mockedStore.getActions()[0].payload + const affectedClients = 7 + act(() => onSuccess(affectedClients)) + + await waitFor(() => { + expect(getMessageField()).toHaveValue('') + expect( + screen.queryByTestId('publish-message-submit'), + ).not.toBeInTheDocument() + }) + + expect( + screen.getByText(`Published (${affectedClients})`), + ).toBeInTheDocument() + }) + + it('should hide success badge client count (just "Published") when connection type is cluster', async () => { + mockedConnType = ConnectionType.Cluster + render() + + fireEvent.click(getSubmitBtn()) + const [, , , onSuccess] = mockedStore.getActions()[0].payload + const affectedClients = 123 + act(() => onSuccess(affectedClients)) + + await waitFor(() => { + expect(screen.getByText(/^Published$/)).toBeInTheDocument() + }) + expect( + screen.queryByText(`Published (${affectedClients})`), + ).not.toBeInTheDocument() + }) + + it('should auto-hide success badge after HIDE_BADGE_TIMER and shows submit button again', () => { + render() + + fireEvent.click(getSubmitBtn()) + const [, , , onSuccess] = mockedStore.getActions()[0].payload + act(() => onSuccess(1)) + + expect(screen.getByText(/Published/)).toBeInTheDocument() + + act(() => { + jest.advanceTimersByTime(3000) + }) + + expect(screen.queryByText(/Published/)).not.toBeInTheDocument() + expect(getSubmitBtn()).toBeInTheDocument() + }) + + it('should persist latest channel/message to context on unmount', () => { + const { unmount } = render(, { + store: mockedStore, + }) + + fireEvent.change(getChannelField(), { + target: { value: 'finalCh' }, + }) + fireEvent.change(getMessageField(), { + target: { value: 'finalMsg' }, + }) + + unmount() + + const actions = mockedStore.getActions() + const setCtx = actions.find( + (a: any) => a.type === 'app/setPubSubFieldsContext', ) + expect(setCtx).toBeTruthy() + expect(setCtx.payload).toEqual({ channel: 'finalCh', message: 'finalMsg' }) }) }) diff --git a/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.styles.tsx b/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.styles.tsx new file mode 100644 index 0000000000..4e8d048ae3 --- /dev/null +++ b/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.styles.tsx @@ -0,0 +1,6 @@ +import styled from 'styled-components' +import { Row } from 'uiSrc/components/base/layout/flex' + +export const StyledWrapper = styled(Row)` + /* border-top: 1px solid ${({ theme }) => theme.semantic.color.border.neutral500}; */ +` diff --git a/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.tsx b/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.tsx index 37738f4738..9232ed056c 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.tsx @@ -1,10 +1,4 @@ -import cx from 'classnames' -import React, { - FormEvent, - useEffect, - useRef, - useState, -} from 'react' +import React, { FormEvent, useEffect, useRef, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import { useParams } from 'react-router-dom' import { @@ -15,17 +9,24 @@ import { ConnectionType } from 'uiSrc/slices/interfaces' import { publishMessageAction } from 'uiSrc/slices/pubsub/pubsub' import { useConnectionType } from 'uiSrc/components/hooks/useConnectionType' -import { FlexItem, Row } from 'uiSrc/components/base/layout/flex' +import { Col, FlexItem, Row } from 'uiSrc/components/base/layout/flex' import { PrimaryButton } from 'uiSrc/components/base/forms/buttons' import { FormField } from 'uiSrc/components/base/forms/FormField' -import { RiBadge } from 'uiSrc/components/base/display/badge/RiBadge' -import { CheckThinIcon } from 'uiSrc/components/base/icons' -import { RiIcon } from 'uiSrc/components/base/icons/RiIcon' +import { ToastCheckIcon, Icon } from 'uiSrc/components/base/icons' import { TextInput } from 'uiSrc/components/base/inputs' -import styles from './styles.module.scss' +import { Text } from 'uiSrc/components/base/text' +import { HorizontalSpacer, Spacer } from 'uiSrc/components/base/layout' const HIDE_BADGE_TIMER = 3000 +const PublishedMessages = ({ clients }: { clients: number | undefined }) => { + if (typeof clients !== 'number') { + return Published + } + + return Published ({clients}) +} + const PublishMessage = () => { const { channel: channelContext, message: messageContext } = useSelector(appContextPubSub) @@ -79,73 +80,68 @@ const PublishMessage = () => { } return ( -
- - - + + + + + Channel name + - setChannel(value) - } + onChange={(value) => setChannel(value)} autoComplete="off" data-testid="field-channel-name" /> - - - - <> - - setMessage(value) - } - autoComplete="off" - data-testid="field-message" - /> - - {connectionType !== ConnectionType.Cluster && ( - - - {affectedClients} - - - - )} - - - - + + + + + + Message + + setMessage(value)} + autoComplete="off" + data-testid="field-message" + /> + - - - - - Publish - - + + {isShowBadge && ( + + + + + )} + + {!isShowBadge && ( + + + + Publish + + + + )}
) diff --git a/redisinsight/ui/src/pages/pub-sub/components/publish-message/styles.module.scss b/redisinsight/ui/src/pages/pub-sub/components/publish-message/styles.module.scss deleted file mode 100644 index f646330b08..0000000000 --- a/redisinsight/ui/src/pages/pub-sub/components/publish-message/styles.module.scss +++ /dev/null @@ -1,46 +0,0 @@ -.container { - .channelWrapper { - min-width: 180px; - } - .messageWrapper { - flex-grow: 3 !important; - position: relative; - - .messageField { - &.showBadge { - padding-right: 80px; - } - } - } - - .badge { - position: absolute; - background-color: var(--pubSubClientsBadge) !important; - top: 50%; - right: 8px; - transform: translateY(-50%); - color: var(--htmlColor) !important; - opacity: 0; - pointer-events: none; - transition: opacity 250ms ease-in-out; - - &.show { - opacity: 1; - pointer-events: auto; - } - - :global(.euiBadge__text) { - display: flex; - align-items: center; - } - - .affectedClients { - margin-left: 6px; - } - - .iconUserBadge { - color: var(--htmlColor) !important; - margin-bottom: 2px; - } - } -} diff --git a/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.spec.tsx b/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.spec.tsx new file mode 100644 index 0000000000..d37a75d205 --- /dev/null +++ b/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.spec.tsx @@ -0,0 +1,134 @@ +// SubscribeForm.spec.tsx +import React from 'react' +import { fireEvent, waitFor } from '@testing-library/react' +import { cloneDeep } from 'lodash' +import { toggleSubscribeTriggerPubSub } from 'uiSrc/slices/pubsub/pubsub' +import { + cleanup, + clearStoreActions, + initialStateDefault, + mockStore, + mockedStore, + render, + screen, +} from 'uiSrc/utils/test-utils' + +import SubscribeForm from './SubscribeForm' +import { SubscriptionType } from 'apiSrc/modules/pub-sub/constants' + +let store: typeof mockedStore + +const createTestStateWithPubSub = (pubsubOverrides = {}) => { + const state = cloneDeep(initialStateDefault) + state.pubsub = { + isSubscribed: false, + loading: false, + publishing: false, + error: '', + isSubscribeTriggered: false, + isConnected: false, + subscriptions: [], + messages: [], + count: 0, + ...pubsubOverrides, + } + return state +} + +const renderSubscribeForm = (pubsubOverrides = {}) => { + const initialStoreState = createTestStateWithPubSub(pubsubOverrides) + return render(, { + store: mockStore(initialStoreState), + }) +} + +const getChannelsInput = () => screen.getByTestId('channels-input') +const getSubscribeBtn = () => screen.getByTestId('subscribe-btn') + +beforeEach(() => { + cleanup() + store = cloneDeep(mockedStore) + store.clearActions() +}) + +describe('SubscribeForm', () => { + it('should initialize channels from subscriptions', async () => { + renderSubscribeForm({ + subscriptions: [ + { channel: 'a.*', type: SubscriptionType.PSubscribe }, + { channel: 'b.c', type: SubscriptionType.PSubscribe }, + ], + }) + + await waitFor(() => + expect(screen.getByDisplayValue('a.* b.c')).toBeInTheDocument(), + ) + }) + + it('should use default "*" when no subscriptions', async () => { + renderSubscribeForm({ + subscriptions: [], + }) + + await waitFor(() => + expect(screen.getByDisplayValue('*')).toBeInTheDocument(), + ) + }) + + it('should restore default "*" on blur when empty', async () => { + render() + + fireEvent.change(getChannelsInput(), { + target: { value: '' }, + }) + fireEvent.blur(getChannelsInput()) + + await waitFor(() => + expect(screen.getByDisplayValue('*')).toBeInTheDocument(), + ) + }) + + it('should update channels as user types', () => { + render() + fireEvent.change(getChannelsInput(), { + target: { value: 'alpha beta.*' }, + }) + expect(screen.getByDisplayValue('alpha beta.*')).toBeInTheDocument() + }) + + it('should dispatch toggleSubscribe with current channels value', () => { + render() + + fireEvent.change(getChannelsInput(), { + target: { value: 'news.* logs.error' }, + }) + fireEvent.click(getSubscribeBtn()) + + expect(clearStoreActions(mockedStore.getActions())).toEqual( + clearStoreActions([toggleSubscribeTriggerPubSub('news.* logs.error')]), + ) + }) + + it('should disable input when subscribed and shows "Unsubscribe" label', () => { + renderSubscribeForm({ + isSubscribed: true, + }) + + expect(getChannelsInput()).toBeDisabled() + expect(getSubscribeBtn()).toHaveTextContent('Unsubscribe') + }) + + it('should show "Subscribe" label when not subscribed', () => { + renderSubscribeForm({ + isSubscribed: false, + }) + expect(getSubscribeBtn()).toHaveTextContent('Subscribe') + }) + + it('should disable subscribe button when loading', () => { + renderSubscribeForm({ + loading: true, + }) + expect(getSubscribeBtn()).toBeDisabled() + }) +}) diff --git a/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.tsx b/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.tsx new file mode 100644 index 0000000000..df08b7ad1a --- /dev/null +++ b/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.tsx @@ -0,0 +1,69 @@ +import React, { useState } from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { + pubSubSelector, + toggleSubscribeTriggerPubSub, +} from 'uiSrc/slices/pubsub/pubsub' + +import { Button } from 'uiSrc/components/base/forms/buttons' +import { FormField } from 'uiSrc/components/base/forms/FormField' +import { TextInput } from 'uiSrc/components/base/inputs' +import { Row } from 'uiSrc/components/base/layout/flex' +import { DEFAULT_SEARCH_MATCH } from 'uiSrc/constants/api' + +import { UserIcon, IndicatorExcludedIcon } from 'uiSrc/components/base/icons' +import { FlexProps } from 'uiSrc/components/base/layout/flex/flex.styles' + +export interface SubscribeFormProps extends Omit {} + +const SubscribeForm = (props: SubscribeFormProps) => { + const dispatch = useDispatch() + + const { isSubscribed, subscriptions, loading } = useSelector(pubSubSelector) + + const [channels, setChannels] = useState( + subscriptions?.length + ? subscriptions.map((sub) => sub.channel).join(' ') + : DEFAULT_SEARCH_MATCH, + ) + + const onFocusOut = () => { + if (!channels) { + setChannels(DEFAULT_SEARCH_MATCH) + } + } + + const toggleSubscribe = () => { + dispatch(toggleSubscribeTriggerPubSub(channels)) + } + + return ( + + + setChannels(value)} + onBlur={onFocusOut} + placeholder="Enter Pattern" + aria-label="channel names for filtering" + data-testid="channels-input" + style={{ minWidth: 250 }} + /> + + + + + ) +} + +export default SubscribeForm diff --git a/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/index.ts b/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/index.ts new file mode 100644 index 0000000000..b6aa3927b0 --- /dev/null +++ b/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/index.ts @@ -0,0 +1,3 @@ +import SubscribeForm from './SubscribeForm' + +export default SubscribeForm diff --git a/redisinsight/ui/src/pages/pub-sub/styles.module.scss b/redisinsight/ui/src/pages/pub-sub/styles.module.scss deleted file mode 100644 index 85f8f66443..0000000000 --- a/redisinsight/ui/src/pages/pub-sub/styles.module.scss +++ /dev/null @@ -1,24 +0,0 @@ -.main { - margin: 0 16px 0; - height: 100%; - display: flex; - flex-direction: column; - - .tableWrapper { - width: 100%; - height: calc(100% - 125px); - padding: 18px 0 0 18px; - - :global(.ReactVirtualized__Grid) { - @include eui.scrollBar; - } - } -} - -.onboardAnchor { - align-self: end; -} - -.onboardPanel { - margin-right: 15px; // Dirty placement fix, to position popover in the bottom right corner of the screen, in line with the browser containers -} From bd947a74d656527adb4c22e6cd56c291b11b9acc Mon Sep 17 00:00:00 2001 From: Pavel Angelov Date: Mon, 10 Nov 2025 15:40:52 +0200 Subject: [PATCH 02/12] remove leftover file --- .../components/publish-message/PublishMessage.styles.tsx | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.styles.tsx diff --git a/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.styles.tsx b/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.styles.tsx deleted file mode 100644 index 4e8d048ae3..0000000000 --- a/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.styles.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import styled from 'styled-components' -import { Row } from 'uiSrc/components/base/layout/flex' - -export const StyledWrapper = styled(Row)` - /* border-top: 1px solid ${({ theme }) => theme.semantic.color.border.neutral500}; */ -` From 06bce1042e078ef37cf007e88f392436d013dbf3 Mon Sep 17 00:00:00 2001 From: Pavel Angelov Date: Mon, 10 Nov 2025 15:51:15 +0200 Subject: [PATCH 03/12] address styles issues --- redisinsight/ui/src/pages/pub-sub/PubSubPage.styles.tsx | 9 +++++++++ redisinsight/ui/src/pages/pub-sub/PubSubPage.tsx | 6 +++--- .../EmptyMessagesList/EmptyMessagesList.styles.tsx | 7 +++++++ .../EmptyMessagesList/EmptyMessagesList.tsx | 9 +++------ .../components/publish-message/PublishMessage.styles.tsx | 8 ++++++++ .../components/publish-message/PublishMessage.tsx | 5 +++-- 6 files changed, 33 insertions(+), 11 deletions(-) create mode 100644 redisinsight/ui/src/pages/pub-sub/PubSubPage.styles.tsx create mode 100644 redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.styles.tsx create mode 100644 redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.styles.tsx diff --git a/redisinsight/ui/src/pages/pub-sub/PubSubPage.styles.tsx b/redisinsight/ui/src/pages/pub-sub/PubSubPage.styles.tsx new file mode 100644 index 0000000000..711bea6f36 --- /dev/null +++ b/redisinsight/ui/src/pages/pub-sub/PubSubPage.styles.tsx @@ -0,0 +1,9 @@ +import styled from 'styled-components' +import { Col } from 'uiSrc/components/base/layout/flex' + +export const OnboardingWrapper = styled(Col)` + align-items: flex-end; + /* Custom margin for onboarding popover */ + /* TODO: Rework the positioning of the onboarding container in order to remove this */ + margin-right: 28px; +` diff --git a/redisinsight/ui/src/pages/pub-sub/PubSubPage.tsx b/redisinsight/ui/src/pages/pub-sub/PubSubPage.tsx index 3034923f5a..d3216d62df 100644 --- a/redisinsight/ui/src/pages/pub-sub/PubSubPage.tsx +++ b/redisinsight/ui/src/pages/pub-sub/PubSubPage.tsx @@ -19,6 +19,7 @@ import { MessagesListWrapper, PublishMessage } from './components' import { Col, FlexItem } from 'uiSrc/components/base/layout/flex' import { Theme } from 'uiSrc/components/base/theme/types' +import { OnboardingWrapper } from './PubSubPage.styles' const FooterPanel = styled(FlexItem)` border-top: 1px solid ${({ theme }) => theme.semantic.color.border.neutral500}; @@ -82,15 +83,14 @@ const PubSubPage = () => { - {/* TODO: Get rid of hardcoded values */} - + - + ) } diff --git a/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.styles.tsx b/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.styles.tsx new file mode 100644 index 0000000000..274b6dea7c --- /dev/null +++ b/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.styles.tsx @@ -0,0 +1,7 @@ +import styled from 'styled-components' +import { RiImage } from 'uiSrc/components/base/display' + +export const HeroImage = styled(RiImage)` + user-select: none; + pointer-events: none; +` diff --git a/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.tsx b/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.tsx index 7c342be2da..1021894408 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.tsx @@ -3,12 +3,13 @@ import styled from 'styled-components' import { ConnectionType } from 'uiSrc/slices/interfaces' import { Text, Title } from 'uiSrc/components/base/text' import { Col, FlexItem } from 'uiSrc/components/base/layout/flex' -import { Banner, RiImage } from 'uiSrc/components/base/display' +import { Banner } from 'uiSrc/components/base/display' import { Spacer } from 'uiSrc/components/base/layout' import { CallOut } from 'uiSrc/components/base/display/call-out/CallOut' import LightBulbImage from 'uiSrc/assets/img/pub-sub/light-bulb.svg' import SubscribeForm from '../../subscribe-form' +import { HeroImage } from './EmptyMessagesList.styles' export interface Props { connectionType?: ConnectionType @@ -39,11 +40,7 @@ const EmptyMessagesList = ({ justify="center" data-testid="empty-messages-list" > - + diff --git a/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.styles.tsx b/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.styles.tsx new file mode 100644 index 0000000000..755487ace6 --- /dev/null +++ b/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.styles.tsx @@ -0,0 +1,8 @@ +import styled from 'styled-components' +import { Col } from 'uiSrc/components/base/layout/flex' + +export const ChannelColumn = styled(Col)` + // There are 2 columns next to each other. + // The channel one doesn't grow, but it should have a minimum width. + min-width: 250px; +` diff --git a/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.tsx b/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.tsx index 9232ed056c..32c5f33f3e 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.tsx @@ -16,6 +16,7 @@ import { ToastCheckIcon, Icon } from 'uiSrc/components/base/icons' import { TextInput } from 'uiSrc/components/base/inputs' import { Text } from 'uiSrc/components/base/text' import { HorizontalSpacer, Spacer } from 'uiSrc/components/base/layout' +import { ChannelColumn } from './PublishMessage.styles' const HIDE_BADGE_TIMER = 3000 @@ -83,7 +84,7 @@ const PublishMessage = () => {
- + Channel name @@ -97,7 +98,7 @@ const PublishMessage = () => { data-testid="field-channel-name" /> - + From 9ab3bfcce285014f81161703ed6ec04365fede00 Mon Sep 17 00:00:00 2001 From: Pavel Angelov Date: Mon, 10 Nov 2025 15:54:33 +0200 Subject: [PATCH 04/12] use gap instead of spacers --- .../EmptyMessagesList/EmptyMessagesList.tsx | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.tsx b/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.tsx index 1021894408..ef367ee69e 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.tsx @@ -39,34 +39,29 @@ const EmptyMessagesList = ({ align="center" justify="center" data-testid="empty-messages-list" + gap="xxl" > - + + You are not subscribed - You are not subscribed + - - - - Subscribe to the Channel to see all the messages published to your - database - - - + + Subscribe to the Channel to see all the messages published to your + database + + - - Running in production may decrease performance and memory available. {connectionType === ConnectionType.Cluster && isSpublishNotSupported && ( <> - - Date: Mon, 10 Nov 2025 16:02:14 +0200 Subject: [PATCH 05/12] more issues addressed --- .../MessageListWrapper.styles.tsx | 4 +-- .../messages-list/MessagesListWrapper.tsx | 30 +++++++------------ 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessageListWrapper.styles.tsx b/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessageListWrapper.styles.tsx index 4981fbae37..dfa1aaab02 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessageListWrapper.styles.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessageListWrapper.styles.tsx @@ -1,7 +1,7 @@ import styled from 'styled-components' -import { Col, FlexItem } from 'uiSrc/components/base/layout/flex' +import { Col } from 'uiSrc/components/base/layout/flex' -export const InnerContainer = styled(FlexItem)` +export const InnerContainer = styled(Col)` background-color: ${({ theme }) => theme.semantic.color.background.neutral300}; border-radius: ${({ theme }) => theme.core.space.space100}; diff --git a/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessagesListWrapper.tsx b/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessagesListWrapper.tsx index d703adedd4..31ada6df49 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessagesListWrapper.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessagesListWrapper.tsx @@ -13,7 +13,7 @@ import MessagesList from './MessagesList' import { Row } from 'uiSrc/components/base/layout/flex' import { Text } from 'uiSrc/components/base/text' import { RiBadge } from 'uiSrc/components/base/display/badge/RiBadge' -import { Spacer, HorizontalSpacer } from 'uiSrc/components/base/layout' +import { HorizontalSpacer } from 'uiSrc/components/base/layout' import SubscribeForm from '../subscribe-form' import { InnerContainer, Wrapper } from './MessageListWrapper.styles' @@ -48,23 +48,21 @@ const MessagesListWrapper = () => { return ( - + Messages: - {messages.length} - + Status: - - + - + Timestamp @@ -73,20 +71,12 @@ const MessagesListWrapper = () => { Message - - {hasMessages && ( - <> - - {({ width, height }) => ( - - )} - - + + {({ width, height }) => ( + + )} + )} {!hasMessages && ( From 6bcaf8378bc40401a8e1c86709c33e1d8e00f226 Mon Sep 17 00:00:00 2001 From: Pavel Angelov Date: Tue, 11 Nov 2025 15:04:20 +0200 Subject: [PATCH 06/12] ternary instead of different component --- .../publish-message/PublishMessage.tsx | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.tsx b/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.tsx index 32c5f33f3e..3d74181e6c 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.tsx @@ -20,14 +20,6 @@ import { ChannelColumn } from './PublishMessage.styles' const HIDE_BADGE_TIMER = 3000 -const PublishedMessages = ({ clients }: { clients: number | undefined }) => { - if (typeof clients !== 'number') { - return Published - } - - return Published ({clients}) -} - const PublishMessage = () => { const { channel: channelContext, message: messageContext } = useSelector(appContextPubSub) @@ -80,6 +72,9 @@ const PublishMessage = () => { dispatch(publishMessageAction(instanceId, channel, message, onSuccess)) } + const getClientsText = (clients?: number) => + typeof clients !== 'number' ? 'Published' : `Published (${clients})` + return ( @@ -120,13 +115,13 @@ const PublishMessage = () => { {isShowBadge && ( - + {getClientsText( connectionType !== ConnectionType.Cluster ? affectedClients - : undefined - } - /> + : undefined, + )} + )} From 50b2e35dd8b6fe21eff9d92d3335541257ac2156 Mon Sep 17 00:00:00 2001 From: Pavel Angelov Date: Tue, 11 Nov 2025 15:09:57 +0200 Subject: [PATCH 07/12] remove unnecessary evaluations --- .../pages/pub-sub/components/subscribe-form/SubscribeForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.tsx b/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.tsx index df08b7ad1a..8afa59aac9 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.tsx @@ -21,7 +21,7 @@ const SubscribeForm = (props: SubscribeFormProps) => { const { isSubscribed, subscriptions, loading } = useSelector(pubSubSelector) - const [channels, setChannels] = useState( + const [channels, setChannels] = useState(() => subscriptions?.length ? subscriptions.map((sub) => sub.channel).join(' ') : DEFAULT_SEARCH_MATCH, From 22cf87cb70dc0c6697c439dece73f8f1ede0fa90 Mon Sep 17 00:00:00 2001 From: Pavel Angelov Date: Tue, 11 Nov 2025 15:19:12 +0200 Subject: [PATCH 08/12] RI-7693: Add patterns counting in Pub/Sub (#5165) --- .../messages-list/MessagesListWrapper.tsx | 22 +++++++++++++--- .../patternsInfo/PatternsInfo.spec.tsx | 1 + .../patternsInfo/PatternsInfo.tsx | 25 +++++++++++-------- .../components => }/patternsInfo/index.ts | 0 .../subscription-panel/SubscriptionPanel.tsx | 4 +-- .../patternsInfo/styles.module.scss | 9 ------- 6 files changed, 36 insertions(+), 25 deletions(-) rename redisinsight/ui/src/pages/pub-sub/components/{subscription-panel/components => }/patternsInfo/PatternsInfo.spec.tsx (99%) rename redisinsight/ui/src/pages/pub-sub/components/{subscription-panel/components => }/patternsInfo/PatternsInfo.tsx (61%) rename redisinsight/ui/src/pages/pub-sub/components/{subscription-panel/components => }/patternsInfo/index.ts (100%) delete mode 100644 redisinsight/ui/src/pages/pub-sub/components/subscription-panel/components/patternsInfo/styles.module.scss diff --git a/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessagesListWrapper.tsx b/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessagesListWrapper.tsx index 31ada6df49..8355a7c5f0 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessagesListWrapper.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessagesListWrapper.tsx @@ -7,6 +7,7 @@ import { pubSubSelector } from 'uiSrc/slices/pubsub/pubsub' import { isVersionHigherOrEquals } from 'uiSrc/utils' import { CommandsVersions } from 'uiSrc/constants/commandsVersions' import { useConnectionType } from 'uiSrc/components/hooks/useConnectionType' +import { DEFAULT_SEARCH_MATCH } from 'uiSrc/constants/api' import EmptyMessagesList from './EmptyMessagesList' import MessagesList from './MessagesList' @@ -15,6 +16,7 @@ import { Text } from 'uiSrc/components/base/text' import { RiBadge } from 'uiSrc/components/base/display/badge/RiBadge' import { HorizontalSpacer } from 'uiSrc/components/base/layout' import SubscribeForm from '../subscribe-form' +import PatternsInfo from '../patternsInfo' import { InnerContainer, Wrapper } from './MessageListWrapper.styles' const SubscribeStatus = ({ isSubscribed }: { isSubscribed: boolean }) => { @@ -26,10 +28,18 @@ const SubscribeStatus = ({ isSubscribed }: { isSubscribed: boolean }) => { } const MessagesListWrapper = () => { - const { messages = [], isSubscribed } = useSelector(pubSubSelector) + const { + messages = [], + isSubscribed, + subscriptions, + } = useSelector(pubSubSelector) const connectionType = useConnectionType() const { version } = useSelector(connectedInstanceOverviewSelector) + const channels = subscriptions?.length + ? subscriptions.map((sub) => sub.channel).join(' ') + : DEFAULT_SEARCH_MATCH + const [isSpublishNotSupported, setIsSpublishNotSupported] = useState(true) @@ -48,9 +58,13 @@ const MessagesListWrapper = () => { return ( - - Messages: - {messages.length} + + + + + Messages: + {messages.length} + diff --git a/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/components/patternsInfo/PatternsInfo.spec.tsx b/redisinsight/ui/src/pages/pub-sub/components/patternsInfo/PatternsInfo.spec.tsx similarity index 99% rename from redisinsight/ui/src/pages/pub-sub/components/subscription-panel/components/patternsInfo/PatternsInfo.spec.tsx rename to redisinsight/ui/src/pages/pub-sub/components/patternsInfo/PatternsInfo.spec.tsx index dfd817ec34..4778e8f4da 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/components/patternsInfo/PatternsInfo.spec.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/patternsInfo/PatternsInfo.spec.tsx @@ -11,6 +11,7 @@ describe('PatternsInfo', () => { const content = 'hello' render() expect(screen.getByText('Patterns: 1')).toBeInTheDocument() + fireEvent.focus(screen.getByTestId('append-info-icon')) await waitFor(() => screen.getAllByText(content)) expect(screen.getAllByText(content)[0]).toBeInTheDocument() diff --git a/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/components/patternsInfo/PatternsInfo.tsx b/redisinsight/ui/src/pages/pub-sub/components/patternsInfo/PatternsInfo.tsx similarity index 61% rename from redisinsight/ui/src/pages/pub-sub/components/subscription-panel/components/patternsInfo/PatternsInfo.tsx rename to redisinsight/ui/src/pages/pub-sub/components/patternsInfo/PatternsInfo.tsx index b9ac92becd..d5aaf9fbd3 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/components/patternsInfo/PatternsInfo.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/patternsInfo/PatternsInfo.tsx @@ -1,10 +1,11 @@ import React from 'react' -import { DEFAULT_SEARCH_MATCH } from 'uiSrc/constants/api' -import { Text } from 'uiSrc/components/base/text' import { RiTooltip } from 'uiSrc/components' +import { DEFAULT_SEARCH_MATCH } from 'uiSrc/constants/api' +import { Text } from 'uiSrc/components/base/text' import { RiIcon } from 'uiSrc/components/base/icons/RiIcon' -import styles from './styles.module.scss' +import { Row } from 'uiSrc/components/base/layout/flex' +import { HorizontalSpacer } from 'uiSrc/components/base/layout' export interface PatternsInfoProps { channels?: string @@ -17,29 +18,33 @@ const PatternsInfo = ({ channels }: PatternsInfoProps) => { } return ( -
- - Patterns: {getChannelsCount()}{' '} + + + Patterns: {getChannelsCount()} + + + {channels ?.trim() .split(' ') - .map((ch) =>

{ch}

)} + .map((ch) => {ch})} } >
-
+
) } diff --git a/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/components/patternsInfo/index.ts b/redisinsight/ui/src/pages/pub-sub/components/patternsInfo/index.ts similarity index 100% rename from redisinsight/ui/src/pages/pub-sub/components/subscription-panel/components/patternsInfo/index.ts rename to redisinsight/ui/src/pages/pub-sub/components/patternsInfo/index.ts diff --git a/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/SubscriptionPanel.tsx b/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/SubscriptionPanel.tsx index 0dab4bfe29..16801d009d 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/SubscriptionPanel.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/SubscriptionPanel.tsx @@ -24,9 +24,9 @@ import { RiTooltip } from 'uiSrc/components' import { AllIconsType, RiIcon } from 'uiSrc/components/base/icons/RiIcon' import { TextInput } from 'uiSrc/components/base/inputs' import { FormField } from 'uiSrc/components/base/forms/FormField' -import PatternsInfo from './components/patternsInfo' import ClickableAppendInfo from './components/clickable-append-info' import styles from './styles.module.scss' +import PatternsInfo from '../patternsInfo' const SubscriptionPanel = () => { const { messages, isSubscribed, subscriptions, loading, count } = @@ -110,7 +110,7 @@ const SubscriptionPanel = () => { setChannels(value)} + onChange={(value) => setChannels(value)} onBlur={onFocusOut} placeholder="Enter Pattern" aria-label="channel names for filtering" diff --git a/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/components/patternsInfo/styles.module.scss b/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/components/patternsInfo/styles.module.scss deleted file mode 100644 index 7b7d2a8dae..0000000000 --- a/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/components/patternsInfo/styles.module.scss +++ /dev/null @@ -1,9 +0,0 @@ -.patternsContainer { - display: flex; - align-items: center; -} - -.appendIcon { - color: var(--iconsDefaultColor) !important; - margin-left: 4px; -} From 8dc5b00c407225e0c5afa680325f88d0623f1c50 Mon Sep 17 00:00:00 2001 From: Pavel Angelov Date: Tue, 11 Nov 2025 15:20:45 +0200 Subject: [PATCH 09/12] RI-7693: Add subscribe patterns explanation (#5166) --- .../subscribe-form/SubscribeForm.tsx | 3 + .../SubscribeInformation.spec.tsx} | 9 +-- .../SubscribeInformation.tsx | 55 ++++++++++++++++ .../components/subscribe-information/index.ts | 3 + .../subscription-panel/SubscriptionPanel.tsx | 6 +- .../ClickableAppendInfo.tsx | 63 ------------------- .../components/clickable-append-info/index.ts | 3 - .../clickable-append-info/styles.module.scss | 8 --- 8 files changed, 69 insertions(+), 81 deletions(-) rename redisinsight/ui/src/pages/pub-sub/components/{subscription-panel/components/clickable-append-info/ClickableAppendInfo.spec.tsx => subscribe-information/SubscribeInformation.spec.tsx} (62%) create mode 100644 redisinsight/ui/src/pages/pub-sub/components/subscribe-information/SubscribeInformation.tsx create mode 100644 redisinsight/ui/src/pages/pub-sub/components/subscribe-information/index.ts delete mode 100644 redisinsight/ui/src/pages/pub-sub/components/subscription-panel/components/clickable-append-info/ClickableAppendInfo.tsx delete mode 100644 redisinsight/ui/src/pages/pub-sub/components/subscription-panel/components/clickable-append-info/index.ts delete mode 100644 redisinsight/ui/src/pages/pub-sub/components/subscription-panel/components/clickable-append-info/styles.module.scss diff --git a/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.tsx b/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.tsx index 8afa59aac9..76d6905796 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.tsx @@ -13,6 +13,7 @@ import { DEFAULT_SEARCH_MATCH } from 'uiSrc/constants/api' import { UserIcon, IndicatorExcludedIcon } from 'uiSrc/components/base/icons' import { FlexProps } from 'uiSrc/components/base/layout/flex/flex.styles' +import SubscribeInformation from '../subscribe-information' export interface SubscribeFormProps extends Omit {} @@ -52,6 +53,8 @@ const SubscribeForm = (props: SubscribeFormProps) => { /> + + - - {!!messages.length && ( - - - - - - )} -
- -
- ) -} - -export default SubscriptionPanel diff --git a/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/index.ts b/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/index.ts deleted file mode 100644 index 675861ad29..0000000000 --- a/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import SubscriptionPanel from './SubscriptionPanel' - -export default SubscriptionPanel diff --git a/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/styles.module.scss b/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/styles.module.scss deleted file mode 100644 index afa5c66969..0000000000 --- a/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/styles.module.scss +++ /dev/null @@ -1,30 +0,0 @@ -.buttonSubscribe { - :global(.euiButton__text) { - font-weight: normal !important; - font-size: 12px !important; - } -} - -.container { - height: 30px; - margin: 0 -4px !important; -} - -.iconSubscribe { - width: 18px; - height: 18px; - margin-right: 6px; - - .iconUser { - width: 18px; - height: 18px; - } -} - -.channels { - margin-right: 8px; - - :global(.euiFormControlLayout--compressed .inputAppendIcon > svg) { - height: 31px !important; - } -} From b9a4b2610a87a7f0e571fe727bb1f0c28ac01f15 Mon Sep 17 00:00:00 2001 From: Pavel Angelov Date: Tue, 11 Nov 2025 19:31:46 +0200 Subject: [PATCH 11/12] address PR comments --- .../EmptyMessagesList.styles.tsx | 15 +++++++++++ .../EmptyMessagesList/EmptyMessagesList.tsx | 24 +++--------------- .../messages-list/MessagesListWrapper.tsx | 14 ++++------- .../publish-message/PublishMessage.styles.tsx | 10 +++++++- .../publish-message/PublishMessage.tsx | 25 +++++++++---------- .../SubscribeInformation.styles.tsx | 13 ++++++++++ .../SubscribeInformation.tsx | 18 +++---------- 7 files changed, 60 insertions(+), 59 deletions(-) create mode 100644 redisinsight/ui/src/pages/pub-sub/components/subscribe-information/SubscribeInformation.styles.tsx diff --git a/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.styles.tsx b/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.styles.tsx index 274b6dea7c..0de76fe8a7 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.styles.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.styles.tsx @@ -1,7 +1,22 @@ import styled from 'styled-components' import { RiImage } from 'uiSrc/components/base/display' +import { Col, FlexItem } from 'uiSrc/components/base/layout/flex' export const HeroImage = styled(RiImage)` user-select: none; pointer-events: none; ` + +export const InnerContainer = styled(Col)` + background-color: ${({ theme }) => + theme.semantic.color.background.neutral300}; + border-radius: ${({ theme }) => theme.core.space.space100}; + border: 1px solid ${({ theme }) => theme.semantic.color.border.neutral500}; + padding: ${({ theme }) => theme.core.space.space300}; + height: 100%; +` + +export const Wrapper = styled(FlexItem)` + margin: ${({ theme }) => theme.core.space.space500}; + height: 100%; +` diff --git a/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.tsx b/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.tsx index ef367ee69e..8b9433760d 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/messages-list/EmptyMessagesList/EmptyMessagesList.tsx @@ -1,35 +1,19 @@ import React from 'react' -import styled from 'styled-components' import { ConnectionType } from 'uiSrc/slices/interfaces' import { Text, Title } from 'uiSrc/components/base/text' -import { Col, FlexItem } from 'uiSrc/components/base/layout/flex' +import { Col } from 'uiSrc/components/base/layout/flex' import { Banner } from 'uiSrc/components/base/display' -import { Spacer } from 'uiSrc/components/base/layout' import { CallOut } from 'uiSrc/components/base/display/call-out/CallOut' import LightBulbImage from 'uiSrc/assets/img/pub-sub/light-bulb.svg' import SubscribeForm from '../../subscribe-form' -import { HeroImage } from './EmptyMessagesList.styles' +import { HeroImage, InnerContainer, Wrapper } from './EmptyMessagesList.styles' export interface Props { connectionType?: ConnectionType isSpublishNotSupported: boolean } -const InnerContainer = styled(Col)` - background-color: ${({ theme }) => - theme.semantic.color.background.neutral300}; - border-radius: ${({ theme }) => theme.core.space.space100}; - border: 1px solid ${({ theme }) => theme.semantic.color.border.neutral500}; - padding: ${({ theme }) => theme.core.space.space300}; - height: 100%; -` - -const Wrapper = styled(FlexItem)` - margin: ${({ theme }) => theme.core.space.space500}; - height: 100%; -` - const EmptyMessagesList = ({ connectionType, isSpublishNotSupported, @@ -43,11 +27,9 @@ const EmptyMessagesList = ({ > - + You are not subscribed - - Subscribe to the Channel to see all the messages published to your database diff --git a/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessagesListWrapper.tsx b/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessagesListWrapper.tsx index 8355a7c5f0..f42cb4f5b0 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessagesListWrapper.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/messages-list/MessagesListWrapper.tsx @@ -19,14 +19,6 @@ import SubscribeForm from '../subscribe-form' import PatternsInfo from '../patternsInfo' import { InnerContainer, Wrapper } from './MessageListWrapper.styles' -const SubscribeStatus = ({ isSubscribed }: { isSubscribed: boolean }) => { - if (!isSubscribed) { - return - } - - return -} - const MessagesListWrapper = () => { const { messages = [], @@ -69,7 +61,11 @@ const MessagesListWrapper = () => { Status: - + {isSubscribed ? ( + + ) : ( + + )} diff --git a/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.styles.tsx b/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.styles.tsx index 755487ace6..91ccbcb46e 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.styles.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.styles.tsx @@ -1,8 +1,16 @@ import styled from 'styled-components' -import { Col } from 'uiSrc/components/base/layout/flex' +import { Col, Row } from 'uiSrc/components/base/layout/flex' export const ChannelColumn = styled(Col)` // There are 2 columns next to each other. // The channel one doesn't grow, but it should have a minimum width. min-width: 250px; ` + +export const ButtonWrapper = styled(Row)` + min-width: 100px; +` + +export const ResultWrapper = styled(Row)` + min-height: 36px; +` diff --git a/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.tsx b/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.tsx index 3d74181e6c..ac249647bf 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/publish-message/PublishMessage.tsx @@ -15,8 +15,11 @@ import { FormField } from 'uiSrc/components/base/forms/FormField' import { ToastCheckIcon, Icon } from 'uiSrc/components/base/icons' import { TextInput } from 'uiSrc/components/base/inputs' import { Text } from 'uiSrc/components/base/text' -import { HorizontalSpacer, Spacer } from 'uiSrc/components/base/layout' -import { ChannelColumn } from './PublishMessage.styles' +import { + ButtonWrapper, + ChannelColumn, + ResultWrapper, +} from './PublishMessage.styles' const HIDE_BADGE_TIMER = 3000 @@ -78,10 +81,9 @@ const PublishMessage = () => { return ( - - + + Channel name - { - - - + Message - { {isShowBadge && ( - + {getClientsText( @@ -122,11 +121,11 @@ const PublishMessage = () => { : undefined, )} - + )} {!isShowBadge && ( - + { Publish - + )} diff --git a/redisinsight/ui/src/pages/pub-sub/components/subscribe-information/SubscribeInformation.styles.tsx b/redisinsight/ui/src/pages/pub-sub/components/subscribe-information/SubscribeInformation.styles.tsx new file mode 100644 index 0000000000..873241f479 --- /dev/null +++ b/redisinsight/ui/src/pages/pub-sub/components/subscribe-information/SubscribeInformation.styles.tsx @@ -0,0 +1,13 @@ +import styled from 'styled-components' +import { RiIcon } from 'uiSrc/components/base/icons' + +export const InfoIcon = styled(RiIcon).attrs({ + type: 'InfoIcon', + size: 'l', + 'data-testid': 'append-info-icon', +})` + cursor: 'pointer'; + // TODO: Remove margin-top + // Hack: for some reason this icon has extra height, which breaks flex alignment + margin-top: 4; +` diff --git a/redisinsight/ui/src/pages/pub-sub/components/subscribe-information/SubscribeInformation.tsx b/redisinsight/ui/src/pages/pub-sub/components/subscribe-information/SubscribeInformation.tsx index f705b29a36..4c54836bb4 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/subscribe-information/SubscribeInformation.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/subscribe-information/SubscribeInformation.tsx @@ -6,35 +6,23 @@ import { UTM_CAMPAINGS, UTM_MEDIUMS, } from 'uiSrc/constants/links' -import { RiIcon } from 'uiSrc/components/base/icons/RiIcon' import { Link } from 'uiSrc/components/base/link/Link' import { RiPopover } from 'uiSrc/components/base' import { Col } from 'uiSrc/components/base/layout/flex' -import { Spacer } from 'uiSrc/components/base/layout' +import { InfoIcon } from './SubscribeInformation.styles' const SubscribeInformation = () => ( - } + button={} data-testid="pub-sub-examples" > - + Subscribe to one or more channels or patterns by entering them, separated by spaces. - - Supported glob-style patterns are described  Date: Tue, 11 Nov 2025 20:00:49 +0200 Subject: [PATCH 12/12] more fixes --- .../components/patternsInfo/PatternsInfo.styles.tsx | 12 ++++++++++++ .../pub-sub/components/patternsInfo/PatternsInfo.tsx | 10 ++-------- .../subscribe-form/SubscribeForm.styles.tsx | 6 ++++++ .../components/subscribe-form/SubscribeForm.tsx | 5 ++--- .../SubscribeInformation.styles.tsx | 4 ++-- 5 files changed, 24 insertions(+), 13 deletions(-) create mode 100644 redisinsight/ui/src/pages/pub-sub/components/patternsInfo/PatternsInfo.styles.tsx create mode 100644 redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.styles.tsx diff --git a/redisinsight/ui/src/pages/pub-sub/components/patternsInfo/PatternsInfo.styles.tsx b/redisinsight/ui/src/pages/pub-sub/components/patternsInfo/PatternsInfo.styles.tsx new file mode 100644 index 0000000000..c7195544f5 --- /dev/null +++ b/redisinsight/ui/src/pages/pub-sub/components/patternsInfo/PatternsInfo.styles.tsx @@ -0,0 +1,12 @@ +import styled from 'styled-components' +import { RiIcon } from 'uiSrc/components/base/icons' + +export const InfoIcon = styled(RiIcon).attrs({ + type: 'InfoIcon', + 'data-testid': 'append-info-icon', +})` + cursor: pointer; + // TODO: Remove margin-top + // Hack: for some reason this icon has extra height, which breaks flex alignment + margin-top: 4px; +` diff --git a/redisinsight/ui/src/pages/pub-sub/components/patternsInfo/PatternsInfo.tsx b/redisinsight/ui/src/pages/pub-sub/components/patternsInfo/PatternsInfo.tsx index d5aaf9fbd3..dc638099e5 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/patternsInfo/PatternsInfo.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/patternsInfo/PatternsInfo.tsx @@ -3,9 +3,9 @@ import { RiTooltip } from 'uiSrc/components' import { DEFAULT_SEARCH_MATCH } from 'uiSrc/constants/api' import { Text } from 'uiSrc/components/base/text' -import { RiIcon } from 'uiSrc/components/base/icons/RiIcon' import { Row } from 'uiSrc/components/base/layout/flex' import { HorizontalSpacer } from 'uiSrc/components/base/layout' +import { InfoIcon } from './PatternsInfo.styles' export interface PatternsInfoProps { channels?: string @@ -36,13 +36,7 @@ const PatternsInfo = ({ channels }: PatternsInfoProps) => { } > - + ) diff --git a/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.styles.tsx b/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.styles.tsx new file mode 100644 index 0000000000..23c4329bdb --- /dev/null +++ b/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.styles.tsx @@ -0,0 +1,6 @@ +import styled from 'styled-components' +import { TextInput } from 'uiSrc/components/base/inputs' + +export const TopicNameField = styled(TextInput)` + min-width: 250px; +` diff --git a/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.tsx b/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.tsx index 76d6905796..3a5b5e4997 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/subscribe-form/SubscribeForm.tsx @@ -7,13 +7,13 @@ import { import { Button } from 'uiSrc/components/base/forms/buttons' import { FormField } from 'uiSrc/components/base/forms/FormField' -import { TextInput } from 'uiSrc/components/base/inputs' import { Row } from 'uiSrc/components/base/layout/flex' import { DEFAULT_SEARCH_MATCH } from 'uiSrc/constants/api' import { UserIcon, IndicatorExcludedIcon } from 'uiSrc/components/base/icons' import { FlexProps } from 'uiSrc/components/base/layout/flex/flex.styles' import SubscribeInformation from '../subscribe-information' +import { TopicNameField } from './SubscribeForm.styles' export interface SubscribeFormProps extends Omit {} @@ -41,7 +41,7 @@ const SubscribeForm = (props: SubscribeFormProps) => { return ( - setChannels(value)} @@ -49,7 +49,6 @@ const SubscribeForm = (props: SubscribeFormProps) => { placeholder="Enter Pattern" aria-label="channel names for filtering" data-testid="channels-input" - style={{ minWidth: 250 }} /> diff --git a/redisinsight/ui/src/pages/pub-sub/components/subscribe-information/SubscribeInformation.styles.tsx b/redisinsight/ui/src/pages/pub-sub/components/subscribe-information/SubscribeInformation.styles.tsx index 873241f479..63dde5a5f7 100644 --- a/redisinsight/ui/src/pages/pub-sub/components/subscribe-information/SubscribeInformation.styles.tsx +++ b/redisinsight/ui/src/pages/pub-sub/components/subscribe-information/SubscribeInformation.styles.tsx @@ -6,8 +6,8 @@ export const InfoIcon = styled(RiIcon).attrs({ size: 'l', 'data-testid': 'append-info-icon', })` - cursor: 'pointer'; + cursor: pointer; // TODO: Remove margin-top // Hack: for some reason this icon has extra height, which breaks flex alignment - margin-top: 4; + margin-top: 4px; `