diff --git a/src/components/EditableText/EditableText.tsx b/src/components/EditableText/EditableText.tsx index 9de635159b..2eeb776f24 100644 --- a/src/components/EditableText/EditableText.tsx +++ b/src/components/EditableText/EditableText.tsx @@ -42,6 +42,7 @@ const EditableText: VibeComponent & { data-testid={dataTestId || getTestId(ComponentDefaultTestId.EDITABLE_TEXT, id)} component={Text} typographyClassName={cx(getStyle(styles, camelCase(type + "-" + weight)), styles.typography)} + clearable {...editableTypographyProps} /> ); diff --git a/src/components/EditableText/__tests__/editableText-tests.jest.tsx b/src/components/EditableText/__tests__/editableText-tests.jest.tsx index 32393c00bd..31336470e3 100644 --- a/src/components/EditableText/__tests__/editableText-tests.jest.tsx +++ b/src/components/EditableText/__tests__/editableText-tests.jest.tsx @@ -1,7 +1,8 @@ import React from "react"; -import { fireEvent, render, cleanup, screen } from "@testing-library/react"; +import { fireEvent, render, cleanup, screen, waitFor } from "@testing-library/react"; import "@testing-library/jest-dom"; import EditableText from "../EditableText"; +import { within } from "@storybook/testing-library"; describe("EditableText", () => { afterEach(() => { @@ -50,4 +51,29 @@ describe("EditableText", () => { }); }); }); + + describe("with placeholder", () => { + it("should show a placeholder if provided and input is empty", async () => { + const placeholderText = "Placeholder text"; + const value = "Editable test"; + render(); + + const component = screen.getByRole("button"); + fireEvent.click(component); + + const input = screen.getByRole("input"); + fireEvent.change(input, { + target: { value: "" } + }); + + expect(input).toHaveValue(""); + expect(screen.getByPlaceholderText(placeholderText)).toBeInTheDocument(); + + await waitFor(() => { + fireEvent.keyDown(input, { key: "Enter" }); + }); + + expect(within(screen.getByRole("button")).getByText(placeholderText)).toBeInTheDocument(); + }); + }); }); diff --git a/src/components/EditableTypography/EditableTypography.module.scss b/src/components/EditableTypography/EditableTypography.module.scss index adee4e65db..1e2919eaf3 100644 --- a/src/components/EditableTypography/EditableTypography.module.scss +++ b/src/components/EditableTypography/EditableTypography.module.scss @@ -42,5 +42,9 @@ visibility: hidden; white-space: pre; } + + &.placeholder { + color: var(--secondary-text-color); + } } } diff --git a/src/components/EditableTypography/EditableTypography.tsx b/src/components/EditableTypography/EditableTypography.tsx index 41a49a8120..91c00cabae 100644 --- a/src/components/EditableTypography/EditableTypography.tsx +++ b/src/components/EditableTypography/EditableTypography.tsx @@ -34,6 +34,8 @@ interface EditableTypographyProps extends VibeComponentProps, EditableTypography component: ElementType; /** Controls the style of the typography component in view mode */ typographyClassName: string; + /** Shows placeholder when empty, if provided */ + clearable?: boolean; } const EditableTypography: VibeComponent = forwardRef( @@ -48,6 +50,7 @@ const EditableTypography: VibeComponent = readOnly = false, ariaLabel = "", placeholder, + clearable, typographyClassName, component: TypographyComponent, isEditMode, @@ -90,7 +93,9 @@ const EditableTypography: VibeComponent = function handleInputValueChange() { handleEditModeChange(false); - if (!inputValue || value === inputValue) { + + const shouldShowPlaceholderWhenEmpty = clearable && placeholder; + if ((!inputValue && !shouldShowPlaceholderWhenEmpty) || value === inputValue) { setInputValue(value); return; } @@ -167,7 +172,8 @@ const EditableTypography: VibeComponent = aria-hidden={isEditing} className={cx(styles.typography, typographyClassName, { [styles.hidden]: isEditing, - [styles.disabled]: readOnly + [styles.disabled]: readOnly, + [styles.placeholder]: !inputValue && placeholder })} tabIndex={0} tooltipProps={tooltipProps}