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
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
.displayMode {
display: flex;
align-items: center;
gap: var(--spacing-small);
width: 100%;
min-height: 45px;

.stepNameTextContainer {
display: flex;
flex-direction: column;
gap: var(--spacing-tiny);
flex: 1;
}
}

.editMode {
display: flex;
align-items: center;
gap: var(--spacing-small);
width: 100%;
min-height: 45px;
animation: fadeIn 0.2s ease-out;

.stepNameTextInput {
flex: 1;
min-width: 200px;
margin-bottom: 0;
}
}

@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-2px);
}

to {
opacity: 1;
transform: translateY(0);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
declare namespace EditableStepNameModuleScssNamespace {
export interface IEditableStepNameModuleScss {
displayMode: string;
editMode: string;
fadeIn: string;
stepNameTextContainer: string;
stepNameTextInput: string;
}
}

declare const EditableStepNameModuleScssModule: EditableStepNameModuleScssNamespace.IEditableStepNameModuleScss;

export = EditableStepNameModuleScssModule;
124 changes: 124 additions & 0 deletions chaoscenter/web/src/components/EditableStepName/EditableStepName.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import React from 'react';
import { Color, FontVariation } from '@harnessio/design-system';
import { Button, ButtonSize, ButtonVariation, Text, TextInput, useToaster } from '@harnessio/uicore';
import css from './EditableStepName.module.scss';

export interface EditableStepNameProps {
stepName?: string;
faultName: string;
onSave: (newStepName: string) => Promise<void>;
fontSize?: FontVariation;
showSubtitle?: boolean;
disabled?: boolean;
}

export function EditableStepName({
stepName,
faultName,
onSave,
fontSize = FontVariation.H5,
showSubtitle = true,
disabled = false
}: EditableStepNameProps): React.ReactElement {
const { showError } = useToaster();
const [isEditing, setIsEditing] = React.useState<boolean>(false);
const [editedValue, setEditedValue] = React.useState<string>('');
const [isSaving, setIsSaving] = React.useState<boolean>(false);

const displayName = stepName || faultName;
const shouldShowSubtitle = showSubtitle && stepName && stepName !== faultName;

const handleEditStart = (): void => {
setEditedValue(displayName);
setIsEditing(true);
};

const handleSave = async (): Promise<void> => {
if (editedValue.trim() && editedValue !== stepName) {
setIsSaving(true);
try {
await onSave(editedValue.trim());
setIsEditing(false);
setEditedValue('');
} catch (error) {
showError('Failed to update step name');
} finally {
setIsSaving(false);
}
} else {
setIsEditing(false);
setEditedValue('');
}
};

const handleCancel = (): void => {
setIsEditing(false);
setEditedValue('');
};

const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>): void => {
if (e.key === 'Enter') {
e.stopPropagation();
e.preventDefault();
handleSave();
} else if (e.key === 'Escape') {
e.stopPropagation();
e.preventDefault();
handleCancel();
}
};

if (isEditing) {
return (
<div className={css.editMode}>
<TextInput
wrapperClassName={css.stepNameTextInput}
value={editedValue}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setEditedValue(e.target.value)}
onKeyDown={handleKeyDown}
autoFocus
disabled={isSaving}
/>
<Button
variation={ButtonVariation.ICON}
icon="tick"
size={ButtonSize.SMALL}
onClick={handleSave}
disabled={isSaving}
loading={isSaving}
/>
<Button
variation={ButtonVariation.ICON}
icon="cross"
size={ButtonSize.SMALL}
onClick={handleCancel}
disabled={isSaving}
/>
</div>
);
}

return (
<div className={css.displayMode}>
<div className={css.stepNameTextContainer}>
<Text font={{ variation: fontSize }}>{displayName}</Text>
{shouldShowSubtitle && (
<Text color={Color.GREY_600} font={{ variation: FontVariation.SMALL, italic: true }}>
{faultName}
</Text>
)}
</div>
{!disabled && (
<Button
variation={ButtonVariation.ICON}
icon="Edit"
size={ButtonSize.SMALL}
onClick={handleEditStart}
minimal
/>
)}
</div>
);
}

export default EditableStepName;
Loading
Loading