UX: header search mobile support - follow up (#32306)

### Changes made

#### Styling
1. added `4px` spacing between search input and cancel button
2. increased cancel button size (with padding) to match input height
3. decreased search panel padding by 1/2 from 16px to 8px
4. animation show/hide changed (from vertical to horizontal) and made it
quicker (300ms->200ms)
5. set html unscrollable when search panel is open

#### Dev
1. changed `data-test-` value to be consistent with other element's
attrs values
2. clear search action tag changed from `<a>` to `<DButton>`
This commit is contained in:
Yuriy Kurant
2025-04-17 20:01:47 +08:00
committed by GitHub
parent 918aa0fbec
commit ff334fde8e
9 changed files with 67 additions and 41 deletions

View File

@ -116,6 +116,7 @@ export default class GlimmerHeader extends Component {
handleAnimationComplete() { handleAnimationComplete() {
this.hasClosingAnimation = false; this.hasClosingAnimation = false;
this.search.visible = false; this.search.visible = false;
this.toggleBodyScrolling(false);
} }
@action @action
@ -169,9 +170,11 @@ export default class GlimmerHeader extends Component {
} }
if (this.site.mobileView && this.search.visible) { if (this.site.mobileView && this.search.visible) {
// hide is delayed for the duration of `search-slide-out` animation
this.hasClosingAnimation = true; this.hasClosingAnimation = true;
} else { } else {
this.search.visible = !this.search.visible; this.search.visible = !this.search.visible;
this.toggleBodyScrolling(true);
} }
if (!this.search.visible) { if (!this.search.visible) {

View File

@ -188,9 +188,7 @@ export default class SearchMenu extends Component {
} }
@bind @bind
clearSearch(e) { clearSearch() {
e.stopPropagation();
e.preventDefault();
this.search.activeGlobalSearchTerm = ""; this.search.activeGlobalSearchTerm = "";
this.search.focusSearchInput(); this.search.focusSearchInput();
this.triggerSearch(); this.triggerSearch();
@ -471,8 +469,8 @@ export default class SearchMenu extends Component {
<DButton <DButton
@action={{this.cancelMobileSearch}} @action={{this.cancelMobileSearch}}
@translatedLabel={{i18n "cancel_value"}} @translatedLabel={{i18n "cancel_value"}}
class="btn-flat" class="btn-flat btn-cancel-mobile-search"
data-test-button="cancel-search-mobile" data-test-button="cancel-mobile-search"
/> />
{{/if}} {{/if}}
</div> </div>

View File

@ -3,7 +3,7 @@ import { i18n } from "discourse-i18n";
<template> <template>
<DButton <DButton
class="show-advanced-search btn-transparent" class="btn-transparent show-advanced-search"
data-test-button="show-advanced-search" data-test-button="show-advanced-search"
title={{i18n "search.open_advanced"}} title={{i18n "search.open_advanced"}}
@action={{@openAdvancedSearch}} @action={{@openAdvancedSearch}}

View File

@ -1,16 +1,13 @@
import { on } from "@ember/modifier"; import DButton from "discourse/components/d-button";
import icon from "discourse/helpers/d-icon";
import { i18n } from "discourse-i18n"; import { i18n } from "discourse-i18n";
<template> <template>
<a <DButton
class="clear-search" class="btn-transparent clear-search"
data-test-anchor="clear-search-input" data-test-button="clear-search-input"
aria-label="clear_input" aria-label="clear_input"
title={{i18n "search.clear_search"}} title={{i18n "search.clear_search"}}
href @action={{@clearSearch}}
{{on "click" @clearSearch}} @icon="xmark"
> />
{{icon "xmark"}}
</a>
</template> </template>

View File

@ -15,7 +15,7 @@ export default modifier(
( (
element, element,
posArgs, posArgs,
{ animate = false, onComplete = () => {}, elementSelector, delay = 300 } { animate = false, onComplete = () => {}, elementSelector, delay = 200 }
) => { ) => {
if (animate) { if (animate) {
const targetEl = elementSelector const targetEl = elementSelector

View File

@ -52,7 +52,7 @@ acceptance("Search - Mobile", function (needs) {
"search results are listed on search value present" "search results are listed on search value present"
); );
await click('[data-test-anchor="clear-search-input"]'); await click('[data-test-button="clear-search-input"]');
assert assert
.dom('[data-test-selector="search-menu-results"]') .dom('[data-test-selector="search-menu-results"]')
@ -64,7 +64,7 @@ acceptance("Search - Mobile", function (needs) {
test("with empty input search", async function (assert) { test("with empty input search", async function (assert) {
await visit("/"); await visit("/");
await click("#search-button"); await click("#search-button");
await click('[data-test-button="cancel-search-mobile"]'); await click('[data-test-button="cancel-mobile-search"]');
assert assert
.dom('[data-test-selector="menu-panel"]') .dom('[data-test-selector="menu-panel"]')
@ -82,7 +82,7 @@ acceptance("Search - Mobile", function (needs) {
"search results are listed on search value present" "search results are listed on search value present"
); );
await click('[data-test-button="cancel-search-mobile"]'); await click('[data-test-button="cancel-mobile-search"]');
await click("#search-button"); await click("#search-button");
assert.dom('[data-test-input="search-term"]').hasNoValue(); assert.dom('[data-test-input="search-term"]').hasNoValue();

View File

@ -242,36 +242,44 @@
&.search-menu-panel { &.search-menu-panel {
width: 100vw; width: 100vw;
max-width: 100vw; max-width: 100vw;
padding: 0.5rem 0.5rem 0;
transform-origin: 66%;
transition: height 0.2s ease-in;
&.empty-panel { &.empty-panel {
height: auto; height: 4rem;
} }
&.slide-in { &.slide-in {
animation: search-slidein 0.3s ease-out forwards; animation: search-slide-in 0.2s ease-out forwards;
} }
&.is-destroying { &.is-destroying {
animation: search-slideout 0.3s ease-in forwards; animation: search-slide-out 0.2s ease-in forwards;
} }
@keyframes search-slidein { @keyframes search-slide-in {
from { from {
opacity: 0; opacity: 0;
transform: translateY(-100%); transform: scaleX(0);
} }
} }
@keyframes search-slideout { @keyframes search-slide-out {
80% { from {
transform: scaleX(1);
opacity: 1; opacity: 1;
} }
100% { to {
opacity: 0; opacity: 0;
transform: translateY(-100%); transform: scaleX(0);
} }
} }
.btn-cancel-mobile-search {
padding: 1rem 0.625rem;
}
} }
} }
} }

View File

@ -24,6 +24,19 @@ $search-pad-horizontal: 0.5em;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
gap: 0.25rem;
@include breakpoint("mobile-extra-large") {
--search-input-wrapper-padding: 0.5rem;
position: fixed;
left: var(--search-input-wrapper-padding);
right: var(--search-input-wrapper-padding);
top: var(--search-input-wrapper-padding);
width: calc(100dvw - calc(var(--search-input-wrapper-padding) * 2));
padding-bottom: var(--search-input-wrapper-padding);
background-color: var(--secondary);
z-index: z("base");
}
} }
.search-input { .search-input {
@ -113,6 +126,13 @@ $search-pad-horizontal: 0.5em;
padding-top: $search-pad-vertical; padding-top: $search-pad-vertical;
} }
@include breakpoint("mobile-extra-large") {
&,
&.with-search-term {
padding-top: 3rem;
}
}
.list { .list {
min-width: 100px; min-width: 100px;
@ -403,18 +423,14 @@ $search-pad-horizontal: 0.5em;
align-items: center; align-items: center;
.spinner { .spinner {
width: 12px; width: 0.75rem; // 12px
height: 12px; height: 0.75rem; // 12px
border-width: 2px; border-width: 0.125rem; // 2px
margin: 0 0.5em 0.25em 0; margin: 0 $hpad 0 0;
margin-top: 2px;
} }
.show-advanced-search, .show-advanced-search,
a.clear-search { .clear-search {
display: inline-block;
background-color: transparent;
.d-icon { .d-icon {
color: var(--primary-medium); color: var(--primary-medium);
} }
@ -427,8 +443,12 @@ $search-pad-horizontal: 0.5em;
} }
} }
a.clear-search { .clear-search {
margin-right: 3px; padding-right: 0.25rem; // 4px
}
.show-advanced-search {
padding-left: 0.375rem; // 6px
} }
} }

View File

@ -72,7 +72,7 @@
justify-content: flex-end; justify-content: flex-end;
.show-advanced-search, .show-advanced-search,
a.clear-search { .clear-search {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
} }