Skip to content

Commit 692d43f

Browse files
authored
Update banner to use structured data rather than raw HTML (#13487)
Updates the docs-site banner to be updated by modifying the `sites/docs/src/data/banner.yml` file, specifying entries as either text or link based. This better fits our site implementation and will enable sharing additional setup with dart.dev. Extracted and modified from #13486.
1 parent 98af675 commit 692d43f

5 files changed

Lines changed: 135 additions & 24 deletions

File tree

sites/docs/lib/src/components/layout/banner.dart

Lines changed: 107 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,122 @@
55
import 'package:jaspr/dom.dart';
66
import 'package:jaspr/jaspr.dart';
77

8-
/// The site-wide banner.
9-
class DashBanner extends StatelessComponent {
10-
const DashBanner(this.inlineHtmlContent, {super.key});
8+
/// The information to display in the site banner,
9+
/// as configured in `src/data/banner.yml`.
10+
@immutable
11+
final class BannerContent {
12+
/// The ordered content parts to render in the banner.
13+
final List<BannerPart> parts;
14+
15+
/// Creates banner content from the specified [parts].
16+
const BannerContent({required this.parts});
17+
18+
/// Creates banner content from the parsed [bannerData].
19+
///
20+
/// The [bannerData] list is expected to contain
21+
/// `text`, `link`, or `newLine` entries from `src/data/banner.yml`.
22+
///
23+
/// Throws if any entry has an unsupported structure.
24+
factory BannerContent.fromList(List<Object?> bannerData) => BannerContent(
25+
parts: [
26+
for (final item in bannerData)
27+
switch (item) {
28+
{'text': final String text} => .text(text),
29+
{'link': final Map<Object?, Object?> link} => .link(
30+
text: link['text'] as String,
31+
url: link['url'] as String,
32+
newTab: link['newTab'] as bool? ?? false,
33+
),
34+
{'newLine': _} => const .newLine(),
35+
_ => throw FormatException('Invalid banner item: $item'),
36+
},
37+
],
38+
);
39+
}
40+
41+
/// A single renderable piece of banner content.
42+
@immutable
43+
sealed class BannerPart {
44+
/// Creates a banner content part.
45+
const BannerPart();
46+
47+
/// Creates a text part with the specified [text].
48+
const factory BannerPart.text(String text) = _BannerText;
49+
50+
/// Creates a link part with the specified [text] and [url].
51+
///
52+
/// Unless [newTab] is `true`, the link opens in the same tab.
53+
const factory BannerPart.link({
54+
required String text,
55+
required String url,
56+
bool newTab,
57+
}) = _BannerLink;
58+
59+
/// Creates a new line part that renders a line break.
60+
const factory BannerPart.newLine() = _BannerNewLine;
61+
}
62+
63+
/// Plain text within a site banner.
64+
final class _BannerText extends BannerPart {
65+
/// Creates a text banner part with the specified [text].
66+
const _BannerText(this.text);
67+
68+
/// The text to render in the banner.
69+
final String text;
70+
}
1171

12-
/// The raw, inline HTML content to render in the banner.
72+
/// A link within a site banner.
73+
final class _BannerLink extends BannerPart {
74+
/// Creates a link banner part with the specified [text] and [url].
1375
///
14-
/// This should only be sourced from managed content,
15-
/// such as our checked-in data files.
16-
final String inlineHtmlContent;
76+
/// Unless [newTab] is `true`, the link opens in the same tab.
77+
const _BannerLink({
78+
required this.text,
79+
required this.url,
80+
this.newTab = false,
81+
});
82+
83+
/// The link label to render in the banner.
84+
final String text;
85+
86+
/// The destination URL for this link.
87+
final String url;
88+
89+
/// Whether this link opens in a new browser tab.
90+
final bool newTab;
91+
}
92+
93+
/// A line break within a site banner.
94+
final class _BannerNewLine extends BannerPart {
95+
/// Creates a line break banner part.
96+
const _BannerNewLine();
97+
}
98+
99+
/// A site-wide banner rendered from structured content.
100+
class DashBanner extends StatelessComponent {
101+
/// Creates a site banner that displays the specified [content].
102+
const DashBanner(this.content, {super.key});
103+
104+
/// The structured content to render in this banner.
105+
final BannerContent content;
17106

18107
@override
19108
Component build(BuildContext context) => div(
20109
id: 'site-banner',
21110
attributes: {'role': 'alert'},
22111
[
23112
p([
24-
RawText(inlineHtmlContent),
113+
for (final part in content.parts)
114+
switch (part) {
115+
_BannerText(:final text) => .text(text),
116+
_BannerLink(:final text, :final url, :final newTab) => a(
117+
href: url,
118+
target: newTab ? Target.blank : null,
119+
attributes: newTab ? const {'rel': 'noopener'} : null,
120+
[.text(text)],
121+
),
122+
_BannerNewLine() => const br(),
123+
},
25124
]),
26125
],
27126
);

sites/docs/lib/src/layouts/dash_layout.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import 'package:jaspr/jaspr.dart';
99
import 'package:jaspr_content/jaspr_content.dart';
1010

1111
import '../components/common/client/cookie_notice.dart';
12+
import '../components/layout/banner.dart';
1213
import '../components/layout/footer.dart';
1314
import '../components/layout/header.dart';
1415
import '../components/layout/sidenav.dart';
@@ -270,6 +271,21 @@ if (sidenav) {
270271
);
271272
}
272273

274+
/// Builds the banner component for the given [page].
275+
Component? buildBanner(Page page) {
276+
final showBanner =
277+
(page.data.page['showBanner'] as bool?) ??
278+
(page.data.site['showBanner'] as bool?) ??
279+
false;
280+
if (showBanner) {
281+
if (page.data['banner'] case final List<Object?> bannerData) {
282+
return DashBanner(BannerContent.fromList(bannerData));
283+
}
284+
}
285+
286+
return null;
287+
}
288+
273289
/// Builds the speculation rules `<script>` and `<link rel="prefetch">`
274290
/// fallback tags for the given [page].
275291
///

sites/docs/lib/src/layouts/doc_layout.dart

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import 'package:jaspr_content/jaspr_content.dart';
88

99
import '../components/common/page_header.dart';
1010
import '../components/common/prev_next.dart';
11-
import '../components/layout/banner.dart';
1211
import '../components/layout/toc.dart';
1312
import '../components/layout/trailing_content.dart';
1413
import '../models/page_navigation_model.dart';
@@ -48,14 +47,9 @@ class DocLayout extends FlutterDocsLayout {
4847
@override
4948
Component buildBody(Page page, Component child) {
5049
final pageData = page.data.page;
51-
final siteData = page.data.site;
5250

5351
final pageTitle = pageData['title'] as String;
5452
final pageDescription = (pageData['description'] as String?)?.trim();
55-
final showBanner =
56-
(pageData['showBanner'] as bool?) ??
57-
(siteData['showBanner'] as bool?) ??
58-
false;
5953
final navigationData = page.navigationData;
6054

6155
return super.buildBody(
@@ -75,10 +69,7 @@ class DocLayout extends FlutterDocsLayout {
7569
PageNavBar(navigationData),
7670
],
7771
),
78-
if (showBanner)
79-
if (siteData['bannerHtml'] case final String bannerHtml
80-
when bannerHtml.trim().isNotEmpty)
81-
DashBanner(bannerHtml),
72+
?buildBanner(page),
8273
div(classes: 'after-leading-content', [
8374
if (navigationData case PageNavigationData(
8475
toc: final toc?,

sites/docs/src/data/banner.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# The following entries are rendered in order as part of the site banner.
2+
# Three types of entries are supported:
3+
# - `text` for plain text.
4+
# - `link` with `text`, `url`, and optional `newTab: true`.
5+
# - `newLine: true` for inserting a line break.
6+
7+
- text: "Help improve Flutter! "
8+
- link:
9+
text: "Take our Q2 survey"
10+
url: https://google.qualtrics.com/jfe/form/SV_3drKjSfjNeLZfq6?Source=Website
11+
newTab: true

sites/docs/src/data/site.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,6 @@ email: flutter-dev@googlegroups.com
55

66
showBanner: true
77

8-
# The raw, inline HTML to display in the banner.
9-
# Is automatically wrapped in a paragraph tag.
10-
bannerHtml: >-
11-
Help improve Flutter!
12-
<a href="https://google.qualtrics.com/jfe/form/SV_3drKjSfjNeLZfq6?Source=Website" target="_blank" rel="noopener">Take our Q2 survey</a>
13-
148
branch: main
159
repo:
1610
organization: https://github.com/flutter

0 commit comments

Comments
 (0)