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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions frontend/src/assets/connectors/logos/aws-dynamo-db.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ import type React from 'react';

export const AWSDynamoDbLogo = (props: React.SVGProps<SVGSVGElement>) => (
<svg
height="256px"
preserveAspectRatio="xMidYMid"
version="1.1"
viewBox="0 0 256 256"
width="256px"
xlinkHref="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
{...props}
Expand Down
2 changes: 0 additions & 2 deletions frontend/src/assets/connectors/logos/gnu-logo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ import type React from 'react';

export const GnuLogo = (props: React.SVGProps<SVGSVGElement>) => (
<svg
height="251px"
preserveAspectRatio="xMidYMid"
version="1.1"
viewBox="0 0 256 251"
width="256px"
xlinkHref="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
{...props}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
DialogTitle,
} from 'components/redpanda-ui/components/dialog';
import { Link } from 'components/redpanda-ui/components/typography';
import { DialogCloseButton } from 'components/ui/dialog-close-button';
import type { ComponentList } from 'protogen/redpanda/api/dataplane/v1/pipeline_pb';

import { ConnectTiles } from './connect-tiles';
Expand All @@ -26,12 +27,16 @@ export const AddConnectorDialog = ({
connectorType,
onAddConnector,
components,
title,
searchPlaceholder,
}: {
isOpen: boolean;
onCloseAddConnector: () => void;
connectorType?: ConnectComponentType | ConnectComponentType[];
onAddConnector: ((connectionName: string, connectionType: ConnectComponentType) => void) | undefined;
components: ComponentList;
title?: string;
searchPlaceholder?: string;
}) => {
let typeFilter: ConnectComponentType[] | undefined;
if (Array.isArray(connectorType)) {
Expand All @@ -44,9 +49,10 @@ export const AddConnectorDialog = ({

return (
<Dialog onOpenChange={onCloseAddConnector} open={isOpen}>
<DialogContent size="xl">
<DialogContent showCloseButton={false} size="xl">
<DialogCloseButton />
<DialogHeader>
<DialogTitle>Add a connector</DialogTitle>
<DialogTitle>{title ?? 'Add a connector'}</DialogTitle>
<DialogDescription className="mt-4">
Configure your pipeline.{' '}
{docsUrl ? (
Expand All @@ -64,6 +70,7 @@ export const AddConnectorDialog = ({
gridCols={3}
hideHeader
onChange={onAddConnector}
searchPlaceholder={searchPlaceholder}
variant="ghost"
/>
</DialogBody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
DialogHeader,
DialogTitle,
} from 'components/redpanda-ui/components/dialog';
import { DialogCloseButton } from 'components/ui/dialog-close-button';
import { QuickAddSecrets } from 'components/ui/secret/quick-add-secrets';
import { AlertTriangle } from 'lucide-react';
import { Scope } from 'protogen/redpanda/api/dataplane/v1/secret_pb';
Expand Down Expand Up @@ -40,7 +41,8 @@ export const AddSecretsDialog = ({

return (
<Dialog onOpenChange={onClose} open={isOpen}>
<DialogContent size="xl">
<DialogContent showCloseButton={false} size="xl">
<DialogCloseButton />
<DialogHeader>
<DialogTitle>Add secrets</DialogTitle>
<DialogDescription>Add secrets to your pipeline.</DialogDescription>
Expand All @@ -59,6 +61,7 @@ export const AddSecretsDialog = ({
</Alert>
)}
<QuickAddSecrets
cardVariant="outlined"
enableNewSecrets
existingSecrets={existingSecrets}
onError={handleError}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,14 @@ export const AddTopicStep = forwardRef<BaseStepRef<AddTopicFormData>, AddTopicSt
}, [form]);

useImperativeHandle(ref, () => ({
triggerSubmit: async () => {
triggerSubmit: async (signal?: AbortSignal) => {
if (signal?.aborted) {
return { success: false };
}
const isValid = await form.trigger();
if (signal?.aborted) {
return { success: false };
}
if (isValid) {
const data = form.getValues();
return handleSubmit(data);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ export const AddUserStep = forwardRef<UserStepRef, AddUserStepProps & MotionProp
username: defaultUsername || '',
password: generatePassword(30, false),
saslMechanism: defaultSaslMechanism || 'SCRAM-SHA-256',
superuser: true,
grantTopicPermissions: true,
specialCharactersEnabled: false,
passwordLength: 30,
consumerGroup: defaultConsumerGroup || '',
Expand Down Expand Up @@ -360,14 +360,20 @@ export const AddUserStep = forwardRef<UserStepRef, AddUserStepProps & MotionProp
}, [form]);

useImperativeHandle(ref, () => ({
triggerSubmit: async () => {
triggerSubmit: async (signal?: AbortSignal) => {
if (signal?.aborted) {
return { success: false };
}

if (authMethod === AuthenticationMethod.SERVICE_ACCOUNT) {
// Service account doesn't use form validation
const userData = form.getValues(); // Pass dummy data
const userData = form.getValues();
return handleSubmit(userData);
}

const isUserFormValid = await form.trigger();
if (signal?.aborted) {
return { success: false };
}
if (isUserFormValid) {
const userData = form.getValues();
return handleSubmit(userData);
Expand Down Expand Up @@ -512,7 +518,7 @@ export const AddUserStep = forwardRef<UserStepRef, AddUserStepProps & MotionProp
)}
</div>

{existingUserSelected && userSelectionType === CreatableSelectionOptions.CREATE && (
{existingUserSelected && userSelectionType === CreatableSelectionOptions.CREATE && !isPending && (
<Alert variant="info">
<AlertDescription>
A user named <b>{watchedUsername}</b> already exists. A reference to the existing user will
Expand Down Expand Up @@ -661,7 +667,7 @@ export const AddUserStep = forwardRef<UserStepRef, AddUserStepProps & MotionProp
<FormField
control={form.control}
disabled={isPending || isReadOnly}
name="superuser"
name="grantTopicPermissions"
render={({ field }) => (
<FormItem>
<div className="flex flex-col gap-2">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,14 @@ import { ConnectorLogo } from './connector-logo';
import type { ConnectComponentSpec } from '../types/schema';
import { componentStatusToString } from '../utils/schema';

const logoStyle = {
height: '24px',
};

const getLogoForComponent = (component: ConnectComponentSpec) => {
if (component?.logoUrl) {
return <img alt={component.name} src={component.logoUrl} style={logoStyle} />;
return <img alt={component.name} className="h-6 w-6 object-contain" src={component.logoUrl} />;
}
if (componentLogoMap[component.name as ComponentName]) {
return <ConnectorLogo name={component.name as ComponentName} style={logoStyle} />;
return <ConnectorLogo className="h-6 w-6" name={component.name as ComponentName} />;
}
return <Waypoints className="text-muted-foreground" style={logoStyle} />;
return <Waypoints className="h-6 w-6 text-muted-foreground" />;
};

const logoMotionProps: MotionProps = {
Expand Down Expand Up @@ -65,9 +61,11 @@ export const ConnectTile = ({
value={component.name}
>
{/* padding right to compensate for the absolute position of the logo */}
<div className="relative flex h-full w-full items-center gap-2 pr-8">
<div className="flex flex-col gap-1">
<InlineCode className="truncate bg-background px-0 py-0 font-semibold text-md">{component.name}</InlineCode>
<div className="relative flex h-full w-full items-center gap-2 pr-10">
<div className="flex min-w-0 flex-col gap-1">
<InlineCode className="break-words bg-background px-0 py-0 font-semibold text-md leading-tight">
{component.name}
</InlineCode>
<span>
{(component.status === ComponentStatus.BETA ||
component.status === ComponentStatus.EXPERIMENTAL ||
Expand All @@ -79,7 +77,7 @@ export const ConnectTile = ({
)}
</span>
</div>
<div className="absolute top-1/2 right-0 -translate-y-1/2">
<div className="absolute top-1/2 right-0 flex h-6 w-6 shrink-0 -translate-y-1/2 items-center justify-center">
<AnimatePresence mode="wait">
{checked ? (
<motion.div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ export type ConnectTilesProps = {
tileWrapperClassName?: string;
title?: React.ReactNode;
description?: React.ReactNode;
searchPlaceholder?: string;
};

export const ConnectTiles = memo(
Expand All @@ -197,6 +198,7 @@ export const ConnectTiles = memo(
tileWrapperClassName,
title,
description,
searchPlaceholder,
...motionProps
},
ref
Expand Down Expand Up @@ -361,7 +363,7 @@ export const ConnectTiles = memo(
<Input
containerClassName="w-[200px] shrink-0"
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Search connectors..."
placeholder={searchPlaceholder ?? 'Search connectors...'}
value={searchQuery}
>
<InputStart>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { KeyValueField } from 'components/redpanda-ui/components/key-value-field
import { Slider } from 'components/redpanda-ui/components/slider';
import { Textarea } from 'components/redpanda-ui/components/textarea';
import { List, ListItem } from 'components/redpanda-ui/components/typography';
import { DialogCloseButton } from 'components/ui/dialog-close-button';
import { type UseFormReturn, useFormContext } from 'react-hook-form';

import { MAX_TASKS, MIN_TASKS } from '../tasks';
Expand Down Expand Up @@ -217,7 +218,8 @@ export function ConfigDialog({ open, onOpenChange, form, mode }: ConfigDialogPro

return (
<Dialog onOpenChange={onOpenChange} open={open}>
<DialogContent size="lg">
<DialogContent showCloseButton={false} size="lg">
<DialogCloseButton />
<DialogHeader>
<DialogTitle>
{mode === 'create' ? 'Pipeline settings' : mode === 'view' ? 'Pipeline settings' : 'Edit pipeline settings'}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { Separator } from 'components/redpanda-ui/components/separator';
import { Tooltip, TooltipContent, TooltipTrigger } from 'components/redpanda-ui/components/tooltip';
import { Heading, ListItem, Text } from 'components/redpanda-ui/components/typography';
import { DeleteResourceAlertDialog } from 'components/ui/delete-resource-alert-dialog';
import { DialogCloseButton } from 'components/ui/dialog-close-button';
import { extractSecretReferences, getUniqueSecretNames } from 'components/ui/secret/secret-detection';
import { InfoIcon, List } from 'lucide-react';
import type { Pipeline } from 'protogen/redpanda/api/dataplane/v1/pipeline_pb';
Expand Down Expand Up @@ -81,7 +82,8 @@ export function DetailsDialog({ open, onOpenChange, pipeline, onDelete, isDeleti

return (
<Dialog onOpenChange={onOpenChange} open={open}>
<DialogContent size="lg">
<DialogContent showCloseButton={false} size="lg">
<DialogCloseButton />
<DialogHeader>
<DialogTitle>Pipeline details</DialogTitle>
</DialogHeader>
Expand Down
50 changes: 21 additions & 29 deletions frontend/src/components/pages/rp-connect/pipeline/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,14 +159,20 @@ vi.mock('../onboarding/add-connector-dialog', () => ({
// The real component needs secrets/topics/users RPCs which are complex to mock
// Includes variant prop to distinguish dialog vs popover instances
vi.mock('./pipeline-command-menu', async () => ({
PipelineCommandMenu: (props: { open: boolean; onOpenChange: (open: boolean) => void; variant?: string }) => {
PipelineCommandMenu: (props: {
open: boolean;
onOpenChange: (open: boolean) => void;
variant?: string;
initialFilter?: string;
}) => {
if (!props.open) {
return null;
}
const label = props.variant === 'popover' ? 'Slash Menu' : 'Command Menu';
return (
<div data-testid={props.variant === 'popover' ? 'slash-menu' : 'command-menu'} role="dialog">
<span>{label}</span>
{props.initialFilter && <span data-testid="command-menu-filter">{props.initialFilter}</span>}
<button onClick={() => props.onOpenChange(false)} type="button">
Close
</button>
Expand Down Expand Up @@ -559,28 +565,22 @@ describe('PipelinePage', () => {

// ── Command menu ───────────────────────────────────────────────────

it('pressing Cmd+Shift+P opens the variable insert menu', async () => {
it('clicking a sidebar variable button opens the command menu with the correct filter', async () => {
const user = userEvent.setup();
render(<PipelinePage />, { transport: createTransport() });

// Dispatch keyboard event for Cmd+Shift+P
act(() => {
window.dispatchEvent(
new KeyboardEvent('keydown', {
key: 'p',
metaKey: true,
shiftKey: true,
bubbles: true,
})
);
});
// The sidebar "Variables" button opens the command menu filtered to variables
const variablesButton = screen.getByRole('button', { name: /variables/i });
await user.click(variablesButton);

// The mocked PipelineCommandMenu renders "Command Menu" text when open
await waitFor(() => {
expect(screen.getByText('Command Menu')).toBeInTheDocument();
expect(screen.getByTestId('command-menu')).toBeInTheDocument();
expect(screen.getByTestId('command-menu-filter')).toHaveTextContent('variables');
});
});

it('typing / in the editor dismisses an open command menu to avoid overlap', async () => {
const user = userEvent.setup();
// Enable the slash command feature flag for this test
mockIsFeatureFlagEnabled.mockImplementation((flag: string) => flag === 'enableConnectSlashMenu');

Expand All @@ -591,29 +591,21 @@ describe('PipelinePage', () => {
expect(contentChangeListeners.length).toBeGreaterThan(0);
});

// First open the command menu via Cmd+Shift+P
act(() => {
window.dispatchEvent(
new KeyboardEvent('keydown', {
key: 'p',
metaKey: true,
shiftKey: true,
bubbles: true,
})
);
});
// Open the command menu via a sidebar button
const secretsButton = screen.getByRole('button', { name: /secrets/i });
await user.click(secretsButton);

// Verify command menu is open
await waitFor(() => {
expect(screen.getByText('Command Menu')).toBeInTheDocument();
expect(screen.getByTestId('command-menu')).toBeInTheDocument();
});

// Simulate a slash trigger via the mock editor's content change listener.
// The useSlashCommand hook subscribes to onDidChangeModelContent.
// When a '/' is detected, detectSlashTrigger checks getPosition() + getModel().getLineContent().
// Our mock returns position {lineNumber:1, column:4} and lineContent ' /' which means
// slashColumn=3, charBefore=' ' (whitespace) → valid trigger position.
// The hook then calls onOpen (handleSlashOpen) which sets isCommandMenuOpen to false.
// The hook then calls onOpen (handleSlashOpen) which sets commandMenuFilter to null.
act(() => {
for (const cb of contentChangeListeners) {
cb({ changes: [{ text: '/' }] });
Expand All @@ -622,7 +614,7 @@ describe('PipelinePage', () => {

// The command dialog should now be closed
await waitFor(() => {
expect(screen.queryByText('Command Menu')).not.toBeInTheDocument();
expect(screen.queryByTestId('command-menu')).not.toBeInTheDocument();
});
});

Expand Down
Loading
Loading