({
modalProps: {
title: 'Create Topic',
- style: { width: '80%', minWidth: '600px', maxWidth: '1000px', top: '50px' },
+ style: { width: '80%', minWidth: '600px', maxWidth: '1000px', top: '50px', paddingTop: '10px', paddingBottom: '10px' },
okText: 'Create',
successTitle: 'Topic created!',
@@ -548,18 +553,31 @@ function makeCreateTopicModal(parent: TopicList) {
configs: config.filter(x => x.name.length > 0),
});
- return
- Name:{result.topicName}
- Partitions:{String(result.partitionCount).replace('-1', '(Default)')}
- Replication Factor:{String(result.replicationFactor).replace('-1', '(Default)')}
-
+ return (
+
+ Name:
+
+ {result.topicName}
+
+
+ Partitions:
+
+ {String(result.partitionCount).replace('-1', '(Default)')}
+
+ Replication Factor:
+
+ {String(result.replicationFactor).replace('-1', '(Default)')}
+
+
+ )
},
onSuccess: (_state, _result) => {
parent.refreshData(true);
diff --git a/frontend/src/components/pages/topics/Topic.Produce.tsx b/frontend/src/components/pages/topics/Topic.Produce.tsx
index 32e75ef6e..2b7e35776 100644
--- a/frontend/src/components/pages/topics/Topic.Produce.tsx
+++ b/frontend/src/components/pages/topics/Topic.Produce.tsx
@@ -15,7 +15,7 @@ import { Link as ReactRouterLink } from 'react-router-dom'
import { PublishMessagePayloadOptions, PublishMessageRequest } from '../../../protogen/redpanda/api/console/v1alpha1/publish_messages_pb';
import { uiSettings } from '../../../state/ui';
import { appGlobal } from '../../../state/appGlobal';
-import { base64ToUInt8Array, isValidBase64 } from '../../../utils/utils';
+import { base64ToUInt8Array, isValidBase64, substringWithEllipsis } from '../../../utils/utils';
import { isEmbedded } from '../../../config';
type EncodingOption = {
@@ -552,7 +552,8 @@ export class TopicProducePage extends PageComponent<{ topicName: string }> {
const topicName = this.props.topicName;
p.title = 'Produce'
p.addBreadcrumb('Topics', '/topics');
- p.addBreadcrumb(topicName, '/topics/' + topicName);
+ p.addBreadcrumb(substringWithEllipsis(topicName, 50), '/topics/' + topicName);
+
p.addBreadcrumb('Produce record', '/produce-record')
this.refreshData(true);
appGlobal.onRefresh = () => this.refreshData(true);
@@ -566,7 +567,9 @@ export class TopicProducePage extends PageComponent<{ topicName: string }> {
return (
Produce Kafka record
- This will produce a single record to the {this.props.topicName} topic.
+
+ This will produce a single record to the topic.
+
diff --git a/frontend/src/index-cloud-integration.scss b/frontend/src/index-cloud-integration.scss
index 28e258df5..56cafa03b 100644
--- a/frontend/src/index-cloud-integration.scss
+++ b/frontend/src/index-cloud-integration.scss
@@ -28,10 +28,6 @@ div[class^="ant-"],
opacity: 0.6 !important;
}
-// Remove padding
-.pageTitle {
- padding: 0 0 24px 0 !important;
-}
.ant-page-header.has-breadcrumb {
padding: 0;
diff --git a/frontend/src/index.scss b/frontend/src/index.scss
index 6b1c7fd91..c6d3a6711 100644
--- a/frontend/src/index.scss
+++ b/frontend/src/index.scss
@@ -1531,10 +1531,6 @@ div.ant-collapse.ant-collapse-borderless>div>div>span {
background-color: rgba(255, 187, 0, 0.185);
}
-.pageTitle {
- /* background: rgba(255, 0, 0, 0.089); */
-}
-
.ant-breadcrumb {
/* don't allow wrapping */
white-space: nowrap;
diff --git a/frontend/src/state/uiState.ts b/frontend/src/state/uiState.ts
index 65d372b05..097db6028 100644
--- a/frontend/src/state/uiState.ts
+++ b/frontend/src/state/uiState.ts
@@ -14,10 +14,15 @@ import { PageDefinition } from '../components/routes';
import { api } from './backendApi';
import { uiSettings, TopicDetailsSettings as TopicSettings } from './ui';
+export interface BreadcrumbOptions {
+ canBeTruncated?: boolean;
+ canBeCopied?: boolean;
+}
export interface BreadcrumbEntry {
title: string;
linkTo: string;
+ options?: BreadcrumbOptions;
}
diff --git a/frontend/src/utils/utils.test.ts b/frontend/src/utils/utils.test.ts
new file mode 100644
index 000000000..d8fbcfa95
--- /dev/null
+++ b/frontend/src/utils/utils.test.ts
@@ -0,0 +1,31 @@
+import { substringWithEllipsis } from './utils'; // Adjust the import path as needed
+
+describe('substringWithEllipsis', () => {
+ it('returns the original string if its length is less than maxLength', () => {
+ expect(substringWithEllipsis('Hello', 10)).toBe('Hello');
+ });
+
+ it('returns the original string if its length is equal to maxLength', () => {
+ expect(substringWithEllipsis('Hello', 5)).toBe('Hello');
+ });
+
+ it('handles cases where maxLength is less than 3', () => {
+ // Since effectiveLength is calculated as Math.max(maxLength - 3, 1),
+ // a maxLength of 2 would lead to an effectiveLength of 1, and thus the output should be "H..."
+ // However, given the logic, it's adjusted to ensure there's at least 1 character before the ellipsis
+ expect(substringWithEllipsis('Hello, world!', 2)).toBe('H...');
+ expect(substringWithEllipsis('Hello, world!', 1)).toBe('H...');
+ });
+
+ it('returns an empty string with ellipsis if maxLength is 0', () => {
+ // This scenario is interesting because the logic dictates a minimum effective length of 1 character.
+ // However, a maxLength of 0 logically suggests no characters should be shown.
+ // The function's logic needs to be clear on this behavior; assuming we follow the implementation, it would be:
+ expect(substringWithEllipsis('Hello, world!', 0)).toBe('H...');
+ // But if considering maxLength of 0 as a request for no output, the implementation might need adjusting.
+ });
+
+ it('correctly handles an empty input string', () => {
+ expect(substringWithEllipsis('', 5)).toBe('');
+ });
+});
diff --git a/frontend/src/utils/utils.ts b/frontend/src/utils/utils.ts
index 8575ca9b2..5fdc9352c 100644
--- a/frontend/src/utils/utils.ts
+++ b/frontend/src/utils/utils.ts
@@ -806,3 +806,23 @@ export function retrier(operation: () => Promise, { attempts = Infinity, d
* performing indexing
*/
export type ElementOf = T extends (infer E)[] ? E : T extends readonly (infer F)[] ? F : never;
+
+/**
+ * Truncates a string to a specified length and adds an ellipsis (...) if the truncation occurs.
+ *
+ * @param {string} input The string to truncate.
+ * @param {number} maxLength The maximum length of the string, including the ellipsis.
+ * @returns {string} The truncated string with ellipsis if truncation was necessary, otherwise the original string.
+ */
+export function substringWithEllipsis(input: string, maxLength: number): string {
+ // Check if the input length is greater than the maxLength
+ // Note: We account for the length of the ellipsis in the comparison
+ if (input.length > maxLength) {
+ // Subtract 3 from maxLength to accommodate the ellipsis
+ // Ensure maxLength is at least 4 to avoid negative substring lengths
+ const effectiveLength = Math.max(maxLength - 3, 1);
+ return input.substring(0, effectiveLength) + '...';
+ } else {
+ return input;
+ }
+}