docs(docs-infra): always include fragment in search results (#63866)

Always include page fragment/sub-title in the search dialog results.

PR Close #63866
This commit is contained in:
hawkgs 2025-09-17 11:41:40 +03:00 committed by Jessica Janiuk
parent 5f41f141ee
commit 39cbb97dfc
4 changed files with 94 additions and 78 deletions

View file

@ -12,35 +12,49 @@
@if (searchResults().length) {
<ul class="docs-search-results docs-mini-scroll-track">
@for (result of searchResults(); track result.id) {
<li docsSearchItem [item]="result">
<li docsSearchItem [item]="result" class="docs-search-result">
<a
[routerLink]="'/' + result.url | relativeLink: 'pathname'"
[fragment]="result.url | relativeLink: 'hash'"
(click)="history.addItem(result)"
>
<div>
<div class="docs-result-icon-and-type">
<p class="docs-search-result__label">
<!-- Icon -->
<span class="docs-search-result-icon" aria-hidden="true">
<i role="presentation" class="material-symbols-outlined docs-icon-small">
{{ result.type === 'code' ? 'code' : 'description'}}
</i>
</span>
<!-- Results type -->
<span class="docs-search-results__type" [innerHtml]="result.labelHtml"></span>
</div>
<!-- Page title -->
<span [innerHtml]="result.labelHtml"></span>
@if (result.package) {
<span
[innerHTML]="result.package"
class="docs-search-result__label__package"
></span>
}
</p>
@if (result.subLabelHtml) {
<span
class="docs-search-results__type docs-search-results__lvl2"
[innerHtml]="result.subLabelHtml"
>
</span>
<p class="docs-search-result__sub-label">
<span class="docs-search-result-icon" aria-hidden="true">
<i role="presentation" class="material-symbols-outlined docs-icon-small">
grid_3x3
</i>
</span>
<span [innerHtml]="result.subLabelHtml"></span>
</p>
}
@if (result.contentHtml) {
<p class="docs-search-result__content" [innerHtml]="result.contentHtml"></p>
}
</div>
<!-- Page title -->
<span class="docs-result-page-title">{{ result.category }}</span>
<p class="docs-search-result__category">
{{ result.category }}
</p>
</a>
</li>
}

View file

@ -38,11 +38,21 @@ dialog {
margin: 0;
border-block-end: 1px solid var(--senary-contrast);
li {
.docs-search-result {
border-inline-start: 2px solid var(--senary-contrast);
margin-inline-start: 1rem;
padding-inline-end: 1rem;
padding-block: 0.25rem;
display: block;
font-size: 0.875rem;
.docs-search-result-icon {
display: inline-block;
i {
display: flex;
align-items: center;
font-size: 1.2rem;
}
}
/**
* This rule needs ng-deep to be applied to elements that are created via a [innerHTML] binding
@ -61,17 +71,50 @@ dialog {
color: var(--secondary-contrast);
display: flex;
justify-content: space-between;
padding: 1rem;
gap: 0.5rem;
}
.docs-search-result-icon {
i {
display: flex;
align-items: center;
font-size: 1.2rem;
}
&__label,
&__sub-label,
&__content {
transition: color 0.3s ease;
}
&__label,
&__sub-label {
display: flex;
align-items: center;
gap: 0.75rem;
margin: 0;
}
&__label {
font-weight: 600;
flex-wrap: wrap;
&__package {
font-size: 0.75rem;
}
}
&__content,
&__category {
margin: 0;
}
&__sub-label,
&__content {
margin-block-start: 0.375rem;
margin-inline-start: 2rem;
color: var(--quaternary-contrast);
}
&__category {
font-weight: 400;
color: var(--quaternary-contrast);
}
&.active {
background-color: var(--septenary-contrast); // stylelint-disable-line
}
@ -80,44 +123,14 @@ dialog {
&.active {
background-color: var(--octonary-contrast); // stylelint-disable-line
border-inline-start: 2px solid var(--primary-contrast);
a {
span:not(.docs-result-page-title),
.docs-search-results__type {
color: var(--primary-contrast);
i {
color: var(--primary-contrast);
}
}
.docs-search-result__label,
.docs-search-result__sub-label,
.docs-search-result__content {
color: var(--primary-contrast);
}
}
}
.docs-search-result-icon,
.docs-search-results__type,
.docs-result-page-title {
color: var(--quaternary-contrast);
display: inline-block;
font-size: 0.875rem;
transition: color 0.3s ease;
padding: 0.75rem;
padding-inline-end: 0;
}
.docs-search-results__lvl2 {
display: inline-block;
margin-inline-start: 2rem;
padding-block-start: 0;
}
.docs-search-results__lvl3 {
margin-inline-start: 2rem;
padding-block-start: 0;
}
}
.docs-result-page-title {
font-size: 0.875rem;
font-weight: 400;
}
}
@ -127,14 +140,6 @@ dialog {
color: var(--gray-400);
}
.docs-result-icon-and-type {
display: flex;
.docs-search-results__type {
padding-inline-start: 0;
}
}
.docs-search-footer {
display: flex;
align-items: center;

View file

@ -60,6 +60,8 @@ export interface SearchResultItem {
type: 'doc' | 'code';
labelHtml: string | null;
subLabelHtml: string | null;
contentHtml: string | null;
package: string | null;
url: string;
id: string;

View file

@ -144,13 +144,8 @@ export class Search {
return this.getUniqueSearchResultItems(items).map((hitItem: SearchResult): SearchResultItem => {
const content = hitItem._snippetResult.content;
const hierarchy = hitItem._snippetResult.hierarchy;
const type = hitItem.hierarchy.lvl0 === 'Tutorials' ? 'code' : 'doc';
const category = hitItem.hierarchy?.lvl0 ?? null;
const hasSubLabel = content || hierarchy?.lvl2 || hierarchy?.lvl3 || hierarchy?.lvl4;
const subLabelHtml =
category === 'Reference'
? extractPackageNameFromUrl(hitItem.url)
: this.parseLabelToHtml(hasSubLabel ? this.getBestSnippetForMatch(hitItem) : null);
const hasSubLabel = hierarchy?.lvl2 || hierarchy?.lvl3 || hierarchy?.lvl4;
return {
id: hitItem.objectID,
@ -158,18 +153,18 @@ export class Search {
url: hitItem.url,
labelHtml: this.parseLabelToHtml(hitItem._snippetResult.hierarchy?.lvl1?.value ?? ''),
subLabelHtml,
subLabelHtml: this.parseLabelToHtml(
hasSubLabel ? this.getBestSnippetForMatch(hitItem) : null,
),
contentHtml: content ? this.parseLabelToHtml(content.value) : null,
package: category === 'Reference' ? extractPackageNameFromUrl(hitItem.url) : null,
category: hitItem.hierarchy?.lvl0 ?? null,
};
});
}
private getBestSnippetForMatch(result: SearchResult): string {
// if there is content, return it
if (result._snippetResult.content !== undefined) {
return result._snippetResult.content.value;
}
const hierarchy = result._snippetResult.hierarchy;
if (hierarchy === undefined) {
return '';
@ -242,5 +237,5 @@ function extractPackageNameFromUrl(url: string): string | null {
if (extractedSegment == null) {
return null;
}
return `From <code>@angular/${extractedSegment[1]}</code>`;
return `<code>@angular/${extractedSegment[1]}</code>`;
}