Skip to content

Commit c1b4ccf

Browse files
committed
Fix previous release (mainly related to to search bar)
1 parent 814c1da commit c1b4ccf

File tree

4 files changed

+99
-29
lines changed

4 files changed

+99
-29
lines changed

src/SearchBar/SearchButton.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,17 @@ export function SearchButton(props: SearchButtonProps) {
6565
"getIsInputFocused": () => document.activeElement === inputElement
6666
});
6767

68-
observeInputValue(inputElement, () => forceUpdate());
69-
7068
const cleanups: (() => void)[] = [];
7169

70+
{
71+
const { cleanup } = observeInputValue({
72+
inputElement,
73+
"callback": () => forceUpdate()
74+
});
75+
76+
cleanups.push(cleanup);
77+
}
78+
7279
if (isControlledByUser) {
7380
inputElement.addEventListener(
7481
"focus",
@@ -120,7 +127,6 @@ export function SearchButton(props: SearchButtonProps) {
120127
return;
121128
}
122129

123-
inputElement.value = "";
124130
inputElement.blur();
125131
};
126132

src/tools/observeInputValue.ts

Lines changed: 83 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,84 @@
1-
export function observeInputValue(element: HTMLInputElement, callback: (value: string) => void) {
2-
const elementPrototype = Object.getPrototypeOf(element);
3-
const descriptor = Object.getOwnPropertyDescriptor(elementPrototype, "value");
4-
5-
Object.defineProperty(element, "value", {
6-
get: function (...args) {
7-
// @ts-expect-error
8-
return descriptor.get.apply(this, args);
9-
},
10-
set: function (...args) {
11-
// @ts-expect-error
12-
descriptor.set.apply(this, args);
13-
const newValue = this.value;
14-
15-
callback(newValue);
16-
17-
return newValue;
18-
}
19-
});
1+
import { assert } from "tsafe/assert";
2+
3+
// NOTE: The callback is called way too often, it can be called multiple times
4+
// for the same input value. Futhermore the callback is not guaranteed to be called
5+
// as soon as the input value changes.
6+
export function observeInputValue(params: {
7+
inputElement: HTMLInputElement;
8+
callback: () => void;
9+
}): { cleanup: () => void } {
10+
const { inputElement, callback: callback_params } = params;
11+
12+
const cleanups: (() => void)[] = [];
13+
14+
{
15+
const descriptorToRestore = Object.getOwnPropertyDescriptor(inputElement, "value");
16+
17+
cleanups.push(() => {
18+
assert(descriptorToRestore !== undefined);
19+
Object.defineProperty(inputElement, "value", descriptorToRestore);
20+
});
21+
22+
const inputElementPrototype = Object.getPrototypeOf(inputElement);
23+
const descriptor = Object.getOwnPropertyDescriptor(inputElementPrototype, "value");
24+
25+
Object.defineProperty(inputElement, "value", {
26+
"get": function (...args) {
27+
// @ts-expect-error
28+
return descriptor.get.apply(this, args);
29+
},
30+
"set": function (...args) {
31+
// @ts-expect-error
32+
descriptor.set.apply(this, args);
33+
const newValue = this.value;
34+
35+
callback_params();
36+
37+
return newValue;
38+
}
39+
});
40+
}
41+
42+
inputElement.addEventListener(
43+
"input",
44+
(() => {
45+
const callback = (): void => {
46+
callback_params();
47+
};
48+
49+
cleanups.push(() => inputElement.removeEventListener("input", callback));
50+
51+
return callback;
52+
})()
53+
);
54+
55+
inputElement.addEventListener(
56+
"keydown",
57+
(() => {
58+
const callback = (event: KeyboardEvent) => {
59+
if (event.key === "Escape") {
60+
const timer = setTimeout(() => {
61+
cleanups.splice(cleanups.indexOf(cleanup), 1);
62+
callback_params();
63+
}, 50);
64+
65+
const cleanup = () => {
66+
clearTimeout(timer);
67+
};
68+
69+
cleanups.push(cleanup);
70+
}
71+
};
72+
73+
cleanups.push(() => inputElement.removeEventListener("keyup", callback));
74+
75+
return callback;
76+
})()
77+
);
78+
79+
function cleanup() {
80+
cleanups.forEach(cleanup => cleanup());
81+
}
82+
83+
return { cleanup };
2084
}

stories/Header.stories.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ export const WithUncontrolledSearchBar = getStory(
177177
If you you do not plan to provide any realtime hinting to the user as he types the search query you can provide a \`onSearchButtonClick\`
178178
callback that will be called when the user click on the search button or press enter.
179179
180+
> NOTE: There is a bug in the DSFR that prevent te input to be cleared when the user press the escape key.
181+
We hope it will be fixed soon.
182+
180183
\`\`\`tsx
181184
182185
<Header
@@ -283,14 +286,13 @@ function MySearchInput(props: MySearchInputProps) {
283286
placeholder={placeholder}
284287
type={type}
285288
value={search}
289+
// Note: The default behavior for an input of type 'text' is to clear the input value when the escape key is pressed.
290+
// However, due to a bug in @gouvfr/dsfr the escape key event is not propagated to the input element.
291+
// As a result this onChange is not called when the escape key is pressed.
286292
onChange={event => onSearchChange(event.currentTarget.value)}
287-
// A bug in @gouvfr/dsfr is currently preventing the escape key event to be propagated to the input element.
288-
// As a result this onKeyDown is never called when the user press escape and thus is useless.
289-
// In the current state of thing there is no way to clear the search input and lost focus in controlled mode.
290-
// We hope this issue will be resolved soon.
293+
// Same goes for the keydown event so this is useless but we hope the bug will be fixed soon.
291294
onKeyDown={event => {
292295
if (event.key === "Escape") {
293-
onSearchChange("");
294296
inputElement?.blur();
295297
}
296298
}}

stories/SearchBar.stories.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ function MySearchInput(props: MySearchInputProps) {
7777
onChange={event => onSearchChange(event.currentTarget.value)}
7878
onKeyDown={event => {
7979
if (event.key === "Escape") {
80-
onSearchChange("");
8180
inputElement?.blur();
8281
}
8382
}}
@@ -135,7 +134,6 @@ function MySearchInput(props: MySearchInputProps) {
135134
onChange={event => onSearchChange(event.currentTarget.value)}
136135
onKeyDown={event => {
137136
if (event.key === "Escape") {
138-
onSearchChange("");
139137
setInputElement?.blur();
140138
}
141139
}}

0 commit comments

Comments
 (0)