Skip to content

Commit 238f3e2

Browse files
ktranDevtools-frontend LUCI CQ
authored andcommitted
[GM3Restyling] Extend cards to allow for icon and a heading suffix
Some cards in the Settings page require an icon preceding the heading, and an HTMLElement after the heading. This extends the card settings to allow for these two. Drive-by: Change heading to accept strings only, since in some cases we don't have a translation (path in settings pages). Bug: 368238837 Change-Id: I7a8d629f08088f3588cfcbf42f2ab397ba46f9c4 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/5972704 Commit-Queue: Kim-Anh Tran <[email protected]> Reviewed-by: Kateryna Prokopenko <[email protected]>
1 parent c3b3afe commit 238f3e2

File tree

4 files changed

+67
-12
lines changed

4 files changed

+67
-12
lines changed

front_end/ui/components/cards/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ devtools_module("card") {
1818
deps = [
1919
"../../../core/common:bundle",
2020
"../../../ui/lit-html:bundle",
21+
"../icon_button:bundle",
2122
]
2223
}
2324

front_end/ui/components/cards/Card.test.ts

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@ import {
88
renderElementIntoDOM,
99
} from '../../../testing/DOMHelpers.js';
1010

11+
const CONTENT_SLOT = 'content';
12+
const HEADING_SUFFIX_SLOT = 'heading-suffix';
13+
1114
import * as Cards from './cards.js';
1215

13-
function assertCardContent(card: Cards.Card.Card, expectedContent: string[]) {
14-
const slot = getElementWithinComponent(card, 'slot', HTMLSlotElement);
16+
function assertCardContent(card: Cards.Card.Card, slotName: string, expectedContent: string[]) {
17+
const slot = getElementWithinComponent(card, `slot[name="${slotName}"]`, HTMLSlotElement);
1518
const textContents = Array.from(slot.assignedElements()).map(child => child.textContent);
1619
assert.deepStrictEqual(textContents, expectedContent);
1720
}
@@ -29,7 +32,7 @@ describe('Card', () => {
2932
content: [content1, content2],
3033
};
3134

32-
assertCardContent(card, ['content 1', 'content 2']);
35+
assertCardContent(card, CONTENT_SLOT, ['content 1', 'content 2']);
3336
});
3437

3538
it('order of slotted elements matter', () => {
@@ -43,12 +46,12 @@ describe('Card', () => {
4346
card.data = {
4447
content: [content1, content2],
4548
};
46-
assertCardContent(card, ['content 1', 'content 2']);
49+
assertCardContent(card, CONTENT_SLOT, ['content 1', 'content 2']);
4750

4851
card.data = {
4952
content: [content2, content1],
5053
};
51-
assertCardContent(card, ['content 2', 'content 1']);
54+
assertCardContent(card, CONTENT_SLOT, ['content 2', 'content 1']);
5255
});
5356

5457
it('shows heading', () => {
@@ -63,4 +66,27 @@ describe('Card', () => {
6366
assert.instanceOf(heading, HTMLElement);
6467
assert.strictEqual(heading.textContent, 'This is my heading');
6568
});
69+
70+
it('shows heading icon', async () => {
71+
const card = new Cards.Card.Card();
72+
card.data = {
73+
headingIconName: 'folder',
74+
content: [],
75+
};
76+
renderElementIntoDOM(card);
77+
const icon = card.shadowRoot?.querySelector('devtools-icon');
78+
assert.isNotNull(icon);
79+
});
80+
81+
it('shows heading-suffix', () => {
82+
const suffix = document.createElement('span');
83+
suffix.textContent = 'hello';
84+
const card = new Cards.Card.Card();
85+
card.data = {
86+
headingSuffix: suffix,
87+
content: [],
88+
};
89+
renderElementIntoDOM(card);
90+
assertCardContent(card, HEADING_SUFFIX_SLOT, ['hello']);
91+
});
6692
});

front_end/ui/components/cards/Card.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import type * as Common from '../../../core/common/common.js';
65
import * as LitHtml from '../../lit-html/lit-html.js';
76

87
import cardStyles from './card.css.js';
@@ -16,16 +15,28 @@ declare global {
1615
}
1716

1817
export interface CardData {
19-
heading?: Common.UIString.LocalizedString;
18+
heading?: string;
19+
headingIconName?: string;
20+
headingSuffix?: HTMLElement;
2021
content: HTMLElement[];
2122
}
2223
export class Card extends HTMLElement {
2324
#heading?: string;
25+
#headingIconName?: string;
26+
#headingSuffix?: HTMLElement;
2427
#content: HTMLElement[] = [];
2528
readonly #shadow = this.attachShadow({mode: 'open'});
2629

2730
set data(data: CardData) {
2831
this.#heading = data.heading;
32+
this.#headingIconName = data.headingIconName;
33+
34+
this.#headingSuffix?.remove();
35+
if (data.headingSuffix) {
36+
this.#headingSuffix = data.headingSuffix;
37+
data.headingSuffix.slot = 'heading-suffix';
38+
this.append(data.headingSuffix);
39+
}
2940
this.#content.forEach(content => content.remove());
3041
data.content.forEach(content => {
3142
content.slot = 'content';
@@ -44,7 +55,11 @@ export class Card extends HTMLElement {
4455
// clang-format off
4556
LitHtml.render(html`
4657
<div class="card">
47-
<div role="heading" aria-level="2" class="heading">${this.#heading}</div>
58+
<div class="heading-wrapper">
59+
${this.#headingIconName ? html`<devtools-icon class="heading-icon" name=${this.#headingIconName}></devtools-icon>` : LitHtml.nothing}
60+
<div role="heading" aria-level="2" class="heading">${this.#heading}</div>
61+
<slot name="heading-suffix"></slot>
62+
</div>
4863
<slot name="content" class='content-container'></slot>
4964
</div>
5065
`, this.#shadow, {

front_end/ui/components/cards/card.css

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,23 @@
1414
margin: var(--sys-size-3);
1515
}
1616

17+
.heading-wrapper {
18+
display: flex;
19+
white-space: nowrap;
20+
margin-bottom: var(--sys-size-5);
21+
}
22+
1723
.heading {
1824
color: var(--sys-color-on-surface);
1925
font: var(--sys-typescale-body2-medium);
20-
margin-bottom: var(--sys-size-5);
26+
}
27+
28+
.heading-icon {
29+
margin-right: var(--sys-size-3);
30+
}
31+
32+
slot[name="heading-suffix"]::slotted(*) {
33+
margin-left: auto;
2134
}
2235

2336
.content-container {
@@ -28,10 +41,10 @@
2841
background: var(--app-color-card-background);
2942
}
3043

31-
::slotted(*) {
44+
slot[name="content"]::slotted(*) {
3245
padding: var(--sys-size-5);
3346
}
3447

35-
::slotted(*:not(:first-child)) {
36-
border-top: 1px solid var(--sys-color-divider);
48+
slot[name="content"]::slotted(*:not(:first-child)) {
49+
border-top: var(--sys-size-1) solid var(--sys-color-divider);
3750
}

0 commit comments

Comments
 (0)