Skip to content

Commit afb2b0c

Browse files
authored
WEBDEV-7768 Search beta: Tile box shadow customization & smart facet adjustments (#497)
* Switch to lighter tile box shadows * Expose CSS vars for customizing tile shadows * Make clicks on smart facet dropdowns select immediately (unless clicking the caret) * Reintroduce label beside smart facets * Allow the smart facet label to be customized by consumers * Fix behavior for when a smart facet dropdown's label is clicked * Remove unused imports * CSS var for label font weight & small refactor * Make mobile results count appear on a single line * Don't show filter toggle button in mobile view * Make smart facets scrollable & fix deselection bug * Deselect smart facets when clearing all filters * Upgrade off alpha version
1 parent 39349b1 commit afb2b0c

File tree

7 files changed

+151
-72
lines changed

7 files changed

+151
-72
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"@internetarchive/field-parsers": "^1.0.0",
2929
"@internetarchive/histogram-date-range": "^1.4.0",
3030
"@internetarchive/ia-activity-indicator": "^0.0.6",
31-
"@internetarchive/ia-dropdown": "^1.3.10",
31+
"@internetarchive/ia-dropdown": "^1.4.0",
3232
"@internetarchive/iaux-item-metadata": "^1.0.5",
3333
"@internetarchive/infinite-scroller": "^1.0.1",
3434
"@internetarchive/modal-manager": "^2.0.1",

src/collection-browser.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,16 @@ export class CollectionBrowser
162162
*/
163163
@property({ type: Object }) internalFilters?: SelectedFacets;
164164

165+
/**
166+
* Whether to show smart facets (bubbles) along the top of the component
167+
*/
165168
@property({ type: Boolean }) showSmartFacetBar = false;
166169

170+
/**
171+
* Sets the label that is shown in front of the smart facets, if present.
172+
*/
173+
@property({ type: String }) smartFacetBarLabel?: string;
174+
167175
/**
168176
* Whether to show the date picker (above the facets)
169177
*/
@@ -503,6 +511,10 @@ export class CollectionBrowser
503511
this.sortDirection = null;
504512
this.selectedSort = SortField.default;
505513
}
514+
515+
if (this.smartFacetBar) {
516+
this.smartFacetBar.deselectAll();
517+
}
506518
}
507519

508520
/**
@@ -549,7 +561,9 @@ export class CollectionBrowser
549561
.aggregations=${this.dataSource.aggregations}
550562
.selectedFacets=${this.selectedFacets}
551563
.collectionTitles=${this.dataSource.collectionTitles}
564+
.filterToggleShown=${!this.mobileView}
552565
.filterToggleActive=${this.facetPaneVisible}
566+
.label=${this.smartFacetBarLabel}
553567
@facetsChanged=${this.facetsChanged}
554568
@filtersToggled=${() => {
555569
this.facetPaneVisible = !this.facetPaneVisible;
@@ -2507,7 +2521,7 @@ export class CollectionBrowser
25072521
.mobile #left-column {
25082522
width: 100%;
25092523
min-width: 0;
2510-
padding: 0;
2524+
padding: 5px 0;
25112525
border: 0;
25122526
}
25132527
@@ -2580,9 +2594,8 @@ export class CollectionBrowser
25802594
}
25812595
25822596
.mobile #results-total {
2583-
float: right;
2584-
margin-bottom: 0;
2585-
margin-right: 5px;
2597+
position: absolute;
2598+
right: 10px;
25862599
}
25872600
25882601
#big-results-count {

src/collection-facets/smart-facets/smart-facet-bar.ts

Lines changed: 98 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@ import {
99
} from 'lit';
1010
import { repeat } from 'lit/directives/repeat.js';
1111
import { customElement, property, state } from 'lit/decorators.js';
12-
import { msg } from '@lit/localize';
1312
import type { Aggregation, Bucket } from '@internetarchive/search-service';
1413
import type { CollectionTitles } from '../../data-source/models';
15-
import type { FacetOption, SelectedFacets } from '../../models';
14+
import type {
15+
FacetEventDetails,
16+
FacetOption,
17+
FacetState,
18+
SelectedFacets,
19+
} from '../../models';
1620
import { updateSelectedFacetBucket } from '../../utils/facet-utils';
1721
import { SmartQueryHeuristicGroup } from './smart-facet-heuristics';
1822
import type { SmartFacetDropdown } from './smart-facet-dropdown';
@@ -47,8 +51,12 @@ export class SmartFacetBar extends LitElement {
4751
@property({ type: Object })
4852
collectionTitles?: CollectionTitles;
4953

54+
@property({ type: Boolean }) filterToggleShown = false;
55+
5056
@property({ type: Boolean }) filterToggleActive = false;
5157

58+
@property({ type: String }) label?: string;
59+
5260
@state() private heuristicRecs: SmartFacet[] = [];
5361

5462
@state() private smartFacets: SmartFacet[][] = [];
@@ -62,9 +70,13 @@ export class SmartFacetBar extends LitElement {
6270
render() {
6371
if (!this.query) return nothing;
6472

73+
const shouldShowLabel = !!this.label && this.smartFacets.length > 0;
6574
return html`
6675
<div id="smart-facets-container">
6776
${this.filtersToggleTemplate}
77+
${shouldShowLabel
78+
? html`<p id="filters-label">${this.label}</p>`
79+
: nothing}
6880
${repeat(
6981
this.smartFacets,
7082
f =>
@@ -106,6 +118,15 @@ export class SmartFacetBar extends LitElement {
106118
this.updateSmartFacets();
107119
}
108120

121+
deselectAll(): void {
122+
for (const sf of this.smartFacets) {
123+
for (const facet of sf) {
124+
facet.selected = false;
125+
}
126+
}
127+
this.requestUpdate();
128+
}
129+
109130
private async updateSmartFacets(): Promise<void> {
110131
log('updating smart facets');
111132
if (this.query) {
@@ -148,13 +169,15 @@ export class SmartFacetBar extends LitElement {
148169
.facetInfo=${facets}
149170
.labelPrefix=${fieldPrefixes[facets[0].facets[0].facetType]}
150171
.activeFacetRef=${facets[0].facets[0]}
151-
@facetClick=${this.facetDropdownClicked}
152-
@dropdownClick=${this.onDropdownClick}
172+
@facetClick=${this.dropdownOptionClicked}
173+
@dropdownClick=${this.dropdownClicked}
153174
></smart-facet-dropdown>
154175
`;
155176
}
156177

157-
private get filtersToggleTemplate(): TemplateResult {
178+
private get filtersToggleTemplate(): TemplateResult | typeof nothing {
179+
if (!this.filterToggleShown) return nothing;
180+
158181
return html`
159182
<button
160183
id="filters-toggle"
@@ -255,15 +278,48 @@ export class SmartFacetBar extends LitElement {
255278
} as SmartFacet;
256279
}
257280

258-
private facetClicked(e: CustomEvent<SmartFacetEvent>): void {
259-
if (!e.detail.smartFacet.selected) {
281+
/**
282+
* Toggles the state of the given smart facet, and updates the selected facets accordingly.
283+
*/
284+
private toggleSmartFacet(
285+
facet: SmartFacet,
286+
details: FacetEventDetails[],
287+
): void {
288+
let newState: FacetState;
289+
if (facet.selected) {
290+
// When deselected, leave the smart facet where it is
291+
newState = 'none';
292+
this.smartFacets = this.smartFacets.map(f => {
293+
if (f[0] === facet) return [{ ...facet, selected: false }];
294+
return f;
295+
});
296+
} else {
297+
// When selected, move the toggled smart facet to the front of the list
298+
newState = 'selected';
260299
this.smartFacets = [
261-
[{ ...e.detail.smartFacet, selected: true }],
262-
...this.smartFacets.filter(f => f[0] !== e.detail.smartFacet),
300+
[{ ...facet, selected: true }],
301+
...this.smartFacets.filter(f => f[0] !== facet),
263302
];
264303
}
265304

266-
for (const facet of e.detail.details) {
305+
this.updateSelectedFacets(
306+
details.map(facet => ({
307+
...facet,
308+
bucket: {
309+
...facet.bucket,
310+
state: newState,
311+
},
312+
})),
313+
);
314+
}
315+
316+
/**
317+
* Updates the selected facet buckets for each of the given facets,
318+
* and emits a `facetsChanged` event to notify parent components of
319+
* the new state.
320+
*/
321+
private updateSelectedFacets(facets: FacetEventDetails[]): void {
322+
for (const facet of facets) {
267323
this.selectedFacets = updateSelectedFacetBucket(
268324
this.selectedFacets,
269325
facet.facetType,
@@ -278,34 +334,39 @@ export class SmartFacetBar extends LitElement {
278334
this.dispatchEvent(event);
279335
}
280336

281-
private facetDropdownClicked(e: CustomEvent<SmartFacetEvent>): void {
282-
if (
283-
this.smartFacets.find(sf => smartFacetEquals(sf[0], e.detail.smartFacet))
284-
) {
337+
/**
338+
* Handler for when a smart facet button is clicked
339+
*/
340+
private facetClicked(e: CustomEvent<SmartFacetEvent>): void {
341+
this.toggleSmartFacet(e.detail.smartFacet, e.detail.details);
342+
}
343+
344+
/**
345+
* Handler for when an option in a smart facet dropdown menu is selected
346+
*/
347+
private dropdownOptionClicked(e: CustomEvent<SmartFacetEvent>): void {
348+
const existingFacet = this.smartFacets.find(
349+
sf => sf.length === 1 && smartFacetEquals(sf[0], e.detail.smartFacet),
350+
);
351+
if (existingFacet) {
352+
// The facet already exists outside the dropdown, so just select it there
353+
this.toggleSmartFacet(existingFacet[0], e.detail.details);
285354
return;
286355
}
287356

357+
// Otherwise, prepend a new smart facet for the selected option
288358
this.smartFacets = [
289359
[{ ...e.detail.smartFacet, selected: true }],
290360
...this.smartFacets,
291361
];
292362

293-
for (const facet of e.detail.details) {
294-
this.selectedFacets = updateSelectedFacetBucket(
295-
this.selectedFacets,
296-
facet.facetType,
297-
facet.bucket,
298-
true,
299-
);
300-
}
301-
302-
const event = new CustomEvent<SelectedFacets>('facetsChanged', {
303-
detail: this.selectedFacets,
304-
});
305-
this.dispatchEvent(event);
363+
this.updateSelectedFacets(e.detail.details);
306364
}
307365

308-
private onDropdownClick(e: CustomEvent<SmartFacetDropdown>): void {
366+
/**
367+
* Handler for when any dropdown is clicked (whether button, caret, or menu item)
368+
*/
369+
private dropdownClicked(e: CustomEvent<SmartFacetDropdown>): void {
309370
log('smart bar: onDropdownClick', e.detail);
310371
this.shadowRoot
311372
?.querySelectorAll('smart-facet-dropdown')
@@ -330,9 +391,11 @@ export class SmartFacetBar extends LitElement {
330391
#smart-facets-container {
331392
display: flex;
332393
align-items: center;
333-
flex-wrap: wrap;
334394
gap: 5px 10px;
335395
padding: 10px 0;
396+
white-space: nowrap;
397+
overflow: scroll hidden;
398+
scrollbar-width: none;
336399
}
337400
338401
#filters-toggle {
@@ -363,6 +426,12 @@ export class SmartFacetBar extends LitElement {
363426
#filters-toggle.active > svg {
364427
filter: invert(1);
365428
}
429+
430+
#filters-label {
431+
font-size: 1.4rem;
432+
font-weight: var(--smartFacetLabelFontWeight, normal);
433+
margin: 0 -5px 0 0;
434+
}
366435
`;
367436
}
368437
}

src/collection-facets/smart-facets/smart-facet-dropdown.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,21 @@ export class SmartFacetDropdown extends LitElement {
3131
<ia-dropdown
3232
class="dropdown"
3333
displayCaret
34-
openViaButton
3534
closeOnSelect
3635
closeOnEscape
3736
closeOnBackdropClick
3837
includeSelectedOption
38+
usePopover
3939
.options=${this.dropdownOptions}
4040
.selectedOption=${this.activeDropdownOption}
41+
.openViaButton=${false}
4142
@optionSelected=${this.optionSelected}
4243
@click=${this.onDropdownClick}
4344
>
44-
<span class="dropdown-label" slot="dropdown-label"
45+
<span
46+
class="dropdown-label"
47+
slot="dropdown-label"
48+
@click=${this.defaultOptionSelected}
4549
>${this.labelPrefix ?? nothing} ${displayText}</span
4650
>
4751
</ia-dropdown>
@@ -76,13 +80,31 @@ export class SmartFacetDropdown extends LitElement {
7680
);
7781
}
7882

83+
/**
84+
* Handler for when the default option on the dropdown button is clicked
85+
*/
86+
private defaultOptionSelected(): void {
87+
this.handleSelection(this.activeFacetRef?.bucketKey);
88+
}
89+
90+
/**
91+
* Handler for when an option in the dropdown menu is selected
92+
*/
7993
private optionSelected(e: CustomEvent<{ option: optionInterface }>): void {
80-
if (!this.facetInfo || !this.activeFacetRef) return;
94+
this.handleSelection(e.detail.option.id);
95+
}
96+
97+
/**
98+
* Responds to a dropdown selection by emitting a `facetClick` event with
99+
* the appropriate facet details.
100+
*/
101+
private handleSelection(bucketKey?: string): void {
102+
if (!bucketKey || !this.facetInfo || !this.activeFacetRef) return;
81103

82104
let selectedSmartFacet;
83105
for (const smartFacet of this.facetInfo) {
84106
const selectedRef = smartFacet.facets.find(
85-
b => b.bucketKey === e.detail.option.id,
107+
b => b.bucketKey === bucketKey,
86108
);
87109
if (selectedRef) {
88110
this.activeFacetRef = selectedRef;

src/tiles/grid/styles/tile-grid-shared-styles.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export const baseTileStyles = css`
1616
background-color: ${tileBackgroundColor};
1717
border: 1px #2c2c2c;
1818
border-radius: ${tileCornerRadius};
19-
box-shadow: 1px 1px 2px 0;
19+
box-shadow: var(--tileBoxShadow, 1px 1px 2px 0);
2020
box-sizing: border-box;
2121
height: 100%;
2222
display: flex;

src/tiles/tile-dispatcher.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ export class TileDispatcher
433433
}
434434
435435
#container.hoverable:hover {
436-
box-shadow: 0 0 6px 2px rgba(8, 8, 32, 0.8);
436+
box-shadow: var(--tileHoverBoxShadow, 0 0 6px 2px rgba(8, 8, 32, 0.8));
437437
transition: box-shadow 0.1s ease;
438438
}
439439

0 commit comments

Comments
 (0)