Skip to content

Commit 7d93094

Browse files
Fix handleButtonClick to support controlled/uncontrolled inputs and add Storybook stories
Co-authored-by: HenriqueLimas <2222191+HenriqueLimas@users.noreply.github.com>
1 parent 2ece025 commit 7d93094

File tree

2 files changed

+192
-4
lines changed

2 files changed

+192
-4
lines changed
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
import React, { useState } from "react";
2+
import { Meta, StoryFn } from "@storybook/react-vite";
3+
import { EbayFilterInput } from "../index";
4+
5+
const meta: Meta<typeof EbayFilterInput> = {
6+
component: EbayFilterInput,
7+
title: "form input/ebay-filter-input",
8+
argTypes: {
9+
size: {
10+
description: "Filter input size",
11+
table: {
12+
defaultValue: {
13+
summary: "undefined",
14+
},
15+
},
16+
options: ["small", "large"],
17+
control: { type: "select" },
18+
},
19+
placeholder: {
20+
type: "string",
21+
description: "Placeholder text",
22+
table: {
23+
defaultValue: {
24+
summary: "Filter",
25+
},
26+
},
27+
},
28+
a11yClearButton: {
29+
type: "string",
30+
description: "Aria-label for clear button. When provided, shows clear button",
31+
table: {
32+
defaultValue: {
33+
summary: "undefined",
34+
},
35+
},
36+
},
37+
a11yControlsId: {
38+
type: "string",
39+
description: "ID of element that this input controls (for aria-controls)",
40+
},
41+
defaultValue: {
42+
type: "string",
43+
description: "Default value for uncontrolled input",
44+
},
45+
value: {
46+
type: "string",
47+
description: "Value for controlled input",
48+
},
49+
disabled: {
50+
type: "boolean",
51+
description: "Whether the input is disabled",
52+
table: {
53+
defaultValue: {
54+
summary: "false",
55+
},
56+
},
57+
},
58+
onInputChange: {
59+
action: "onInputChange",
60+
description: "Triggered on input value change",
61+
table: {
62+
category: "Events",
63+
defaultValue: {
64+
summary: "(event, { value }) => {}",
65+
},
66+
},
67+
},
68+
onClear: {
69+
action: "onClear",
70+
description: "Triggered when clear button is clicked",
71+
table: {
72+
category: "Events",
73+
defaultValue: {
74+
summary: "(event, { value }) => {}",
75+
},
76+
},
77+
},
78+
},
79+
};
80+
81+
export default meta;
82+
83+
export const Default: StoryFn<typeof EbayFilterInput> = (args) => (
84+
<EbayFilterInput {...args} />
85+
);
86+
87+
export const WithClearButton: StoryFn<typeof EbayFilterInput> = (args) => (
88+
<EbayFilterInput {...args} a11yClearButton="Clear filter" />
89+
);
90+
91+
export const WithDefaultValue: StoryFn<typeof EbayFilterInput> = (args) => (
92+
<EbayFilterInput {...args} defaultValue="Initial value" a11yClearButton="Clear filter" />
93+
);
94+
95+
export const LargeSize: StoryFn<typeof EbayFilterInput> = (args) => (
96+
<EbayFilterInput {...args} size="large" a11yClearButton="Clear filter" />
97+
);
98+
99+
export const SmallSize: StoryFn<typeof EbayFilterInput> = (args) => (
100+
<EbayFilterInput {...args} size="small" a11yClearButton="Clear filter" />
101+
);
102+
103+
export const Disabled: StoryFn<typeof EbayFilterInput> = (args) => (
104+
<EbayFilterInput {...args} disabled a11yClearButton="Clear filter" />
105+
);
106+
107+
export const CustomPlaceholder: StoryFn<typeof EbayFilterInput> = (args) => (
108+
<EbayFilterInput {...args} placeholder="Search items..." a11yClearButton="Clear search" />
109+
);
110+
111+
export const Controlled: StoryFn<typeof EbayFilterInput> = (args) => {
112+
const [value, setValue] = useState("Controlled value");
113+
114+
return (
115+
<div>
116+
<EbayFilterInput
117+
{...args}
118+
value={value}
119+
a11yClearButton="Clear filter"
120+
onInputChange={(e, { value: newValue }) => setValue(newValue)}
121+
onClear={() => setValue("")}
122+
/>
123+
<div style={{ marginTop: "8px" }}>
124+
Current value: <strong>{value}</strong>
125+
</div>
126+
</div>
127+
);
128+
};
129+
130+
export const Uncontrolled: StoryFn<typeof EbayFilterInput> = (args) => {
131+
const [lastValue, setLastValue] = useState("");
132+
133+
return (
134+
<div>
135+
<EbayFilterInput
136+
{...args}
137+
defaultValue="Initial value"
138+
a11yClearButton="Clear filter"
139+
onInputChange={(e, { value }) => setLastValue(value)}
140+
/>
141+
<div style={{ marginTop: "8px" }}>
142+
Last input value: <strong>{lastValue}</strong>
143+
</div>
144+
</div>
145+
);
146+
};
147+
148+
export const WithControlledList: StoryFn<typeof EbayFilterInput> = (args) => {
149+
const [filterValue, setFilterValue] = useState("");
150+
const items = ["Apple", "Banana", "Cherry", "Date", "Elderberry", "Fig", "Grape"];
151+
const filteredItems = items.filter(item =>
152+
item.toLowerCase().includes(filterValue.toLowerCase())
153+
);
154+
155+
return (
156+
<div>
157+
<EbayFilterInput
158+
{...args}
159+
value={filterValue}
160+
a11yClearButton="Clear filter"
161+
a11yControlsId="filtered-list"
162+
placeholder="Filter items..."
163+
onInputChange={(e, { value }) => setFilterValue(value)}
164+
onClear={() => setFilterValue("")}
165+
/>
166+
<ul id="filtered-list" style={{ marginTop: "8px", listStyle: "none", padding: 0 }}>
167+
{filteredItems.length > 0 ? (
168+
filteredItems.map(item => (
169+
<li key={item} style={{ padding: "4px 0" }}>
170+
{item}
171+
</li>
172+
))
173+
) : (
174+
<li style={{ padding: "4px 0", color: "#999" }}>No items found</li>
175+
)}
176+
</ul>
177+
</div>
178+
);
179+
};
180+
181+
export const Collection: StoryFn<typeof EbayFilterInput> = (args) => (
182+
<div style={{ display: "flex", flexDirection: "column", gap: "12px", maxWidth: "400px" }}>
183+
<EbayFilterInput {...args} placeholder="Default" />
184+
<EbayFilterInput {...args} placeholder="With clear button" a11yClearButton="Clear" />
185+
<EbayFilterInput {...args} size="large" placeholder="Large size" a11yClearButton="Clear" />
186+
<EbayFilterInput {...args} size="small" placeholder="Small size" a11yClearButton="Clear" />
187+
<EbayFilterInput {...args} disabled placeholder="Disabled" a11yClearButton="Clear" />
188+
</div>
189+
);

packages/ebayui-core-react/src/ebay-filter-input/filter-input.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,18 @@ const EbayFilterInput: FC<EbayFilterInputProps> = ({
3737
: undefined;
3838

3939
const handleButtonClick = (event: any) => {
40-
// Note: This uses DOM manipulation as a workaround because the textbox component
41-
// manages its own internal state and doesn't expose a proper clear API.
42-
// This is not ideal React code but is necessary for integration with the existing textbox.
40+
// Get the input element - either from ref or by finding it in the DOM
4341
const inputElement = inputRef.current ||
4442
(event.target as HTMLElement).closest('.textbox')?.querySelector('input') as HTMLInputElement;
4543

4644
if (inputElement) {
47-
// Use React's property setter to update the value
45+
// Use React's property setter to update the value properly
4846
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set;
4947
if (nativeInputValueSetter) {
5048
nativeInputValueSetter.call(inputElement, '');
5149

5250
// Dispatch input event to trigger React's onChange handlers
51+
// This works for both controlled and uncontrolled components
5352
const inputEvent = new Event('input', { bubbles: true });
5453
inputElement.dispatchEvent(inputEvent);
5554
}

0 commit comments

Comments
 (0)