Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(variable-input): add custom fields and combobox #44

Merged
merged 13 commits into from
Nov 28, 2024
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,33 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

### [0.0.265](https://github.com/getpingback/ui/compare/v0.0.264...v0.0.265) (2024-11-27)


### Bug Fixes

* **variable-input:** initial content ([9a44ec0](https://github.com/getpingback/ui/commits/9a44ec0db6bc2e8e47056ec24e8939eb21c5b897))

### [0.0.263](https://github.com/getpingback/ui/compare/v0.0.262...v0.0.263) (2024-11-27)


### Bug Fixes

* **variable-input:** reset search on close ([ff0075e](https://github.com/getpingback/ui/commits/ff0075e17bd53aac5ceca5a8f7ea73cb5f85684f))

### [0.0.261](https://github.com/getpingback/ui/compare/v0.0.260...v0.0.261) (2024-11-27)


### Features

* **variable-input:** add custom fields and combobox ([3e89a07](https://github.com/getpingback/ui/commits/3e89a07602d029ad5a8ee5490631503a52706c9a))


### Bug Fixes

* change selection scope ([ec3c5b3](https://github.com/getpingback/ui/commits/ec3c5b31c39522303b26f2ca136d96c819806c8b))
* **variable-input:** remove unused components ([844a5db](https://github.com/getpingback/ui/commits/844a5db2bd55b9fbdebd73dfcc14c1786cc3ae1a))

### [0.0.260](https://github.com/getpingback/ui/compare/v0.0.257...v0.0.260) (2024-11-27)


Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@getpingback/ui",
"author": "Pingback Team",
"version": "0.0.260",
"version": "0.0.266",
"license": "MIT",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
Expand Down
47 changes: 29 additions & 18 deletions src/components/variable-input/variable-input.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,55 @@
import type { Meta, StoryObj } from "@storybook/react";
import type { Meta, StoryObj } from '@storybook/react';

import { VariableInput } from "./variable-input";
import { VariableInput } from './variable-input';

const meta = {
title: "Components/VariableInput",
title: 'Components/VariableInput',
component: VariableInput,
parameters: {},

tags: ["autodocs"],
tags: ['autodocs'],

argTypes: {},
argTypes: {}
} satisfies Meta<typeof VariableInput>;

export default meta;
type Story = StoryObj<typeof meta>;

const options = [
{ label: "Email", value: "email" },
{ label: "Name", value: "name" },
{ label: "Phone", value: "phone" },
{
heading: 'Common Variables',
items: [
{ label: 'Email', value: 'email' },
{ label: 'Name', value: 'name' },
{ label: 'Phone', value: 'phone' }
]
},
{
heading: 'Custom Variables',
items: [{ label: 'Custom Variable', value: 'customVariable' }]
}
];

export const Default: Story = {
args: {
options,
},
options
}
};

export const WithInputProps: Story = {
args: {
label: "URL",
placeholder: "https://www.example.com",
helperText: "Enter the URL of the page you want to redirect to",
options,
},
label: 'URL',
placeholder: 'https://www.example.com',
helperText: 'Enter the URL of the page you want to redirect to',
options
}
};

export const WithInitialContent: Story = {
args: {
initialContent: "Hello {{name}}! How are you?",
options,
},
label: 'Expression',
placeholder: 'Enter the expression',
initialContent: 'Hello {{name}}! How are you?',
options
}
};
98 changes: 74 additions & 24 deletions src/components/variable-input/variable-input.test.tsx
Original file line number Diff line number Diff line change
@@ -1,64 +1,114 @@
import React from "react";
import { render, fireEvent } from "@testing-library/react";
import { VariableInput } from "./variable-input";
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import { VariableInput } from './variable-input';

describe("VariableInput Component", () => {
describe('VariableInput Component', () => {
const options = [
{ label: "Variable 1", value: "var1" },
{ label: "Variable 2", value: "var2" },
{
heading: 'Common Variables',
items: [
{ label: 'Email', value: 'email' },
{ label: 'Name', value: 'name' },
{ label: 'Phone', value: 'phone' }
]
},
{
heading: 'Custom Variables',
items: [{ label: 'Custom Variable', value: 'customVariable' }]
}
];

it("should render with default props and open dropdown on trigger click", () => {
it('should render with default props and open dropdown on trigger click', () => {
const { getByTestId, getByText } = render(<VariableInput options={options} />);

const triggerButton = getByTestId("variable-input-trigger");
const triggerButton = getByTestId('variable-input-trigger');
fireEvent.click(triggerButton);

expect(getByText("Variable 1")).toBeInTheDocument();
expect(getByText("Variable 2")).toBeInTheDocument();
expect(getByText('Email')).toBeInTheDocument();
expect(getByText('Name')).toBeInTheDocument();
expect(getByText('Phone')).toBeInTheDocument();
expect(getByText('Custom Variable')).toBeInTheDocument();
});

it("should call onChangeContent when text is input", () => {
it('should call onChangeContent when text is input', () => {
const handleChangeContent = jest.fn();
const { container } = render(<VariableInput options={options} onChangeContent={handleChangeContent} />);

const editor = container.querySelector("[contenteditable='true']");
if (editor) {
fireEvent.input(editor, { target: { textContent: "Hello" } });
expect(handleChangeContent).toHaveBeenCalledWith("Hello");
fireEvent.input(editor, { target: { textContent: 'Hello' } });
expect(handleChangeContent).toHaveBeenCalledWith('Hello');
}
});

it("should call onSelect when a variable is selected", () => {
it('should call onSelect when a variable is selected', () => {
const handleSelect = jest.fn();
const { getByText, getByTestId } = render(<VariableInput options={options} onSelect={handleSelect} />);
const { getByText, getByTestId } = render(<VariableInput options={options} onSelectVariable={handleSelect} />);

const triggerButton = getByTestId("variable-input-trigger");
const triggerButton = getByTestId('variable-input-trigger');
fireEvent.click(triggerButton);

fireEvent.click(getByText("Variable 1"));
expect(handleSelect).toHaveBeenCalledWith("var1");
fireEvent.click(getByText('Email'));
expect(handleSelect).toHaveBeenCalledWith('email');
});

it("should display placeholder when not focused and empty", () => {
const { container } = render(<VariableInput options={options} placeholder='Enter text' />);
it('should display placeholder when not focused and empty', () => {
const { container } = render(<VariableInput options={options} placeholder="Enter text" />);

const editor = container.querySelector("[contenteditable='true']");
if (editor) {
expect(editor.innerHTML).toContain("Enter text");
expect(editor.innerHTML).toContain('Enter text');
}
});

it("should prevent editing of variable spans", () => {
it('should prevent editing of variable spans', () => {
const { container } = render(<VariableInput options={options} />);
const editor = container.querySelector("[contenteditable='true']");

if (editor) {
fireEvent.input(editor, { target: { innerHTML: '<span data-variable="var1">Variable 1</span>' } });

fireEvent.keyDown(editor, { key: "Backspace" });
fireEvent.keyDown(editor, { key: 'Backspace' });

expect(editor.innerHTML).toContain('Variable 1');
}
});

it('should filter variables when text is typed in the search input', () => {
const { getByTestId, queryByText } = render(<VariableInput options={options} />);

const triggerButton = getByTestId('variable-input-trigger');
fireEvent.click(triggerButton);

const searchInput = getByTestId('variable-input-search-input');
fireEvent.change(searchInput, { target: { value: 'email' } });

expect(queryByText('Email')).toBeInTheDocument();
expect(queryByText('Name')).not.toBeInTheDocument();
expect(queryByText('Phone')).not.toBeInTheDocument();
expect(queryByText('Custom Variable')).not.toBeInTheDocument();
});

it('should show message when no variables are found in the search', () => {
const { getByTestId, getByText } = render(<VariableInput options={options} />);

const triggerButton = getByTestId('variable-input-trigger');
fireEvent.click(triggerButton);

const searchInput = getByTestId('variable-input-search-input');
fireEvent.change(searchInput, { target: { value: 'xyz' } });

expect(editor.innerHTML).toContain("Variable 1");
expect(getByText('No results found')).toBeInTheDocument();
});

it('should prioritize initialContent over placeholder', () => {
const initialContent = 'Initial content';
const { container } = render(<VariableInput options={options} initialContent={initialContent} placeholder="Placeholder" />);

const editor = container.querySelector("[contenteditable='true']");
if (editor) {
expect(editor.innerHTML).toBe(initialContent);
expect(editor.innerHTML).not.toBe('Placeholder');
}
});
});
Loading