From a74e95fbfe47ae37ee700f950116b823c9f4617d Mon Sep 17 00:00:00 2001 From: Ben Brooks <56796209+bensbits91@users.noreply.github.com> Date: Wed, 23 Jul 2025 09:17:47 -0700 Subject: [PATCH] [CL-601] Replace default reset button to enable it in more browsers (#14974) * bb/pm-19497/replace default reset button to enable it in more browsers * address feedback: add ngClass; improve accessibility * add signals for form hover and input focus; compute showResetButton * fix(style): [CL-601] Improve CSS per reviewer comments Signed-off-by: Ben Brooks * fix: [CL-601] add ngForm; remove standalone attributes Signed-off-by: Ben Brooks * fix: [CL-601] add translation strings Signed-off-by: Ben Brooks * fix: [CL-601] Use message key in aria label Signed-off-by: Ben Brooks * fix: [CL-601] Remove unnecessary aria-hidden attribute Signed-off-by: Ben Brooks * fix: [CL-601] Remove unecessary ngForm attributes Signed-off-by: Ben Brooks * fix: [CL-601] Add storybook description Signed-off-by: Ben Brooks * fix: [CL-601] Match main for recent signal input changs Signed-off-by: Ben Brooks --------- Signed-off-by: Ben Brooks --- apps/browser/src/_locales/en/messages.json | 3 ++ apps/desktop/src/locales/en/messages.json | 3 ++ apps/web/src/locales/en/messages.json | 3 ++ .../src/search/search.component.css | 18 ++++++------ .../src/search/search.component.html | 28 +++++++++++++++---- .../components/src/search/search.component.ts | 22 +++++++++++++-- libs/components/src/search/search.mdx | 3 +- 7 files changed, 62 insertions(+), 18 deletions(-) diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 37d64c3416b..a1b41b44bfd 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -547,6 +547,9 @@ "searchVault": { "message": "Search vault" }, + "resetSearch": { + "message": "Reset search" + }, "edit": { "message": "Edit" }, diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index e5e6dcb2882..c269f7d32f8 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -41,6 +41,9 @@ "searchVault": { "message": "Search vault" }, + "resetSearch": { + "message": "Reset search" + }, "addItem": { "message": "Add item" }, diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 5d7cbd7d479..b143d4da56a 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -629,6 +629,9 @@ "searchGroups": { "message": "Search groups" }, + "resetSearch": { + "message": "Reset search" + }, "allItems": { "message": "All items" }, diff --git a/libs/components/src/search/search.component.css b/libs/components/src/search/search.component.css index 35304438a88..6233de2a3ac 100644 --- a/libs/components/src/search/search.component.css +++ b/libs/components/src/search/search.component.css @@ -1,19 +1,17 @@ /** * Tailwind doesn't have a good way to style search-cancel-button. + * Hide the default reset button that only appears in some browsers. */ bit-search input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; - height: 21px; - width: 21px; - margin: 0; - cursor: pointer; - background-repeat: no-repeat; - mask-image: url("./close-button.svg"); - -webkit-mask-image: url("./close-button.svg"); - background-color: rgba(var(--color-text-muted)); } -bit-search input[type="search"]::-webkit-search-cancel-button:hover { - background-color: rgba(var(--color-text-main)); +/** + * Style our custom reset button that works in all common browsers. + * Tailwind CSS does not natively support mask-image or -webkit-mask-image utilities (but can be extended if needed). + */ +.bw-reset-btn { + mask-image: url("./close-button.svg"); + -webkit-mask-image: url("./close-button.svg"); } diff --git a/libs/components/src/search/search.component.html b/libs/components/src/search/search.component.html index 803b61c6322..b1b92fb151a 100644 --- a/libs/components/src/search/search.component.html +++ b/libs/components/src/search/search.component.html @@ -1,9 +1,14 @@ - -
+
+ + diff --git a/libs/components/src/search/search.component.ts b/libs/components/src/search/search.component.ts index ef12e7eead6..c6c5f2757dd 100644 --- a/libs/components/src/search/search.component.ts +++ b/libs/components/src/search/search.component.ts @@ -1,6 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { Component, ElementRef, ViewChild, input, model } from "@angular/core"; +import { NgIf, NgClass } from "@angular/common"; +import { Component, ElementRef, ViewChild, input, model, signal, computed } from "@angular/core"; import { ControlValueAccessor, NG_VALUE_ACCESSOR, @@ -16,6 +17,9 @@ import { FocusableElement } from "../shared/focusable-element"; let nextId = 0; +/** + * Do not nest Search components inside another `
`, as they already contain their own standalone `` element for searching. + */ @Component({ selector: "bit-search", templateUrl: "./search.component.html", @@ -30,7 +34,7 @@ let nextId = 0; useExisting: SearchComponent, }, ], - imports: [InputModule, ReactiveFormsModule, FormsModule, I18nPipe], + imports: [InputModule, ReactiveFormsModule, FormsModule, I18nPipe, NgIf, NgClass], }) export class SearchComponent implements ControlValueAccessor, FocusableElement { private notifyOnChange: (v: string) => void; @@ -43,6 +47,11 @@ export class SearchComponent implements ControlValueAccessor, FocusableElement { // Use `type="text"` for Safari to improve rendering performance protected inputType = isBrowserSafariApi() ? ("text" as const) : ("search" as const); + protected isInputFocused = signal(false); + protected isFormHovered = signal(false); + + protected showResetButton = computed(() => this.isInputFocused() || this.isFormHovered()); + readonly disabled = model(); readonly placeholder = input(); readonly autocomplete = input(); @@ -52,11 +61,20 @@ export class SearchComponent implements ControlValueAccessor, FocusableElement { } onChange(searchText: string) { + this.searchText = searchText; // update the model when the input changes (so we can use it with *ngIf in the template) if (this.notifyOnChange != undefined) { this.notifyOnChange(searchText); } } + // Handle the reset button click + clearSearch() { + this.searchText = ""; + if (this.notifyOnChange) { + this.notifyOnChange(""); + } + } + onTouch() { if (this.notifyOnTouch != undefined) { this.notifyOnTouch(); diff --git a/libs/components/src/search/search.mdx b/libs/components/src/search/search.mdx index 7775225b8c2..98e91162c94 100644 --- a/libs/components/src/search/search.mdx +++ b/libs/components/src/search/search.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Source, Primary, Controls, Title } from "@storybook/addon-docs"; +import { Meta, Canvas, Source, Primary, Controls, Title, Description } from "@storybook/addon-docs"; import * as stories from "./search.stories"; @@ -9,6 +9,7 @@ import { SearchModule } from "@bitwarden/components"; ``` Search field +