Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow the browser-compat frontmatter value to be an array #5972

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 109 additions & 42 deletions build/document-extractor.js
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,9 @@ function addSections($) {
// XXX That `_addSingleSpecialSection(section.clone())` might return a
// and empty array and that means it failed and we should
// bail.
subSections.push(..._addSingleSpecialSection(section.clone()));
for (const item of _addSingleSpecialSection(section.clone())) {
subSections.push(...item);
}
section.empty();
} else {
section.append(child);
Expand All @@ -280,7 +282,10 @@ function addSections($) {
}
return [subSections, flaws];
}
const specialSections = _addSingleSpecialSection($);
const specialSections = [];
for (const item of _addSingleSpecialSection($)) {
specialSections.push(...item);
}

// The _addSingleSpecialSection() function will have sucked up the <h2> or <h3>
// and the `div.bc-data` or `div.bc-specs` to turn it into a special section.
Expand Down Expand Up @@ -341,49 +346,99 @@ function _addSingleSpecialSection($) {
if (!dataQuery && specURLsString === "") {
// I wish there was a good place to log this!
const [proseSections] = _addSectionProse($);
return proseSections;
return [proseSections];
}

const query = dataQuery.replace(/^bcd:/, "");
const { browsers, data } = packageBCD(query);
const queries = [];
// If the value of a browser-compat frontmatter key is a YAML array of
// multiple BCD feature identifiers, the “query” string here will end up
// containing a comma-separated list of the BCD identifiers — so we split
// the “query” value out into a JavaScript array.
queries.push(...query.split(",").map((item) => item.trim()));
// We create and populate the “features” value with an array of objects,
// where each object holds the results of doing a BCD query for one of
// the BCD identifiers in the “query” value.
const features = [];
for (let i = 0; i < queries.length; i++) {
features[i] = {};
const { browsers, data } = packageBCD(queries[i]);
features[i].browsers = browsers;
features[i].data = data;
}

const sectionItems = [];
if (specialSectionType === "browser_compatibility") {
if (data === undefined) {
return [
{
type: specialSectionType,
value: {
title,
id,
isH3,
data: null,
query,
browsers: null,
for (let i = 0; i < queries.length; i++) {
const query = queries[i];
if (features[i].data === undefined) {
sectionItems.push([
{
type: specialSectionType,
value: {
title,
id,
isH3,
data: null,
query,
browsers: null,
},
},
},
];
]);
}
// If we’re iterating through a list of multiple BCD features, we’ll
// end up with multiple “Browser compatibility” headings (titles) in
// the rendered page — unless we suppress rendering of all but the
// first “Browser compatibility” heading (title); “showTitle” is the
// flag we pass down through called code to cause that suppression.
const showTitle = i === 0 ? true : false;
const showTableHeadings = features.length > 1 ? true : false;
// For a “Browser compatibility” section, if we’re iterating through
// a list of multiple BCD features, we end up with multiple tables
// in the rendered page.
//
// In that case, to make it more clear which feature each table is
// for, we output the feature name as a heading before each table.
//
// But if there’s only one feature — only one table — we don’t output
// that feature-name heading. So “showTableHeadings” is the flag we
// pass down through called code to control whether those
// feature-name headings are output before the tables.
sectionItems.push(
_buildSpecialBCDSection(
showTitle,
showTableHeadings,
features[i].browsers,
features[i].data,
query
)
);
}
return _buildSpecialBCDSection();
return sectionItems;
} else if (specialSectionType === "specifications") {
if (data === undefined && specURLsString === "") {
return [
{
type: specialSectionType,
value: {
title,
id,
isH3,
query,
specifications: [],
},
},
];
// If we’re iterating through a list of multiple BCD features, we’ll
// end up with multiple “Specifications” headings (titles) in the
// rendered page — unless we suppress rendering of all but the first
// “Specifications” heading (title); “showTitle” is the flag we pass
// down through called code to cause that suppression.
for (let i = 0; i < queries.length; i++) {
const showTitle = i === 0 ? true : false;
sectionItems.push(
_buildSpecialSpecSection(showTitle, features[i].data, query)
);
}
return _buildSpecialSpecSection();
return sectionItems;
}

throw new Error(`Unrecognized special section type '${specialSectionType}'`);

function _buildSpecialBCDSection() {
function _buildSpecialBCDSection(
showTitle,
showTableHeadings,
browsers,
data,
query
) {
// First extract a map of all release data, keyed by (normalized) browser
// name and the versions.
// You'll have a map that looks like this:
Expand All @@ -408,6 +463,10 @@ function _addSingleSpecialSection($) {
browserReleaseData.set(name, releaseData);
}

if (!data) {
return [];
}

for (const [key, compat] of Object.entries(data)) {
let block;
if (key === "__compat") {
Expand Down Expand Up @@ -449,10 +508,12 @@ function _addSingleSpecialSection($) {
}
}

title = showTitle ? title : null;
return [
{
type: "browser_compatibility",
value: {
showTableHeadings,
title,
id,
isH3,
Expand Down Expand Up @@ -518,14 +579,15 @@ function _addSingleSpecialSection($) {
return version.split(".").map(Number);
}

function _buildSpecialSpecSection() {
function _buildSpecialSpecSection(showTitle, data, query) {
// Collect spec URLs from a BCD feature, a 'spec-urls' value, or both;
// For a BCD feature, it can either be a string or an array of strings.
let specURLs = [];

if (data) {
// If 'data' is non-null, that means we have data for a BCD feature
// that we can extract spec URLs from.
// If 'data' is non-null, that means we have data, from the value of
// a browser-compat frontmatter key, for a BCD feature that we can
// extract spec URLs from.
for (const [key, compat] of Object.entries(data)) {
if (key === "__compat" && compat.spec_url) {
if (Array.isArray(compat.spec_url)) {
Expand All @@ -535,12 +597,16 @@ function _addSingleSpecialSection($) {
}
}
}
}

if (specURLsString !== "") {
// If specURLsString is non-empty, then it has the string contents of
// the document’s 'spec-urls' frontmatter key: one or more URLs.
specURLs.push(...specURLsString.split(",").map((url) => url.trim()));
// If there’s no browser-compat frontmatter key, then we check the
// value of the spec-urls frontmatter key. (Otherwise, we just use
// the BCD identifier(s) from the browser-compat key, and ignore any
// spec-urls key.)
} else {
if (specURLsString !== "") {
// If specURLsString is non-empty, then it has the string contents
// of the document’s 'spec-urls' frontmatter key: one or more URLs.
specURLs.push(...specURLsString.split(",").map((url) => url.trim()));
}
}

// Use BCD specURLs to look up more specification data
Expand Down Expand Up @@ -573,6 +639,7 @@ function _addSingleSpecialSection($) {
})
.filter(Boolean);

title = showTitle ? title : null;
return [
{
type: "specifications",
Expand Down
31 changes: 31 additions & 0 deletions client/src/document/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,37 @@ code {
font-weight: var(--font-body-strong-weight);
}

div.specs p {
border: 1px solid var(--border-primary);
font: var(--type-body-l);
font-size: 0.875rem;
line-height: 1.75;
margin: 0;
padding: 0.5rem 0.75rem;
}

div.specs ~ div.specs p,
div.specs p ~ p {
border-top-style: none;
}

.spec-header {
background: var(--background-tertiary);
border: 1px solid var(--border-primary);
border-bottom-style: none;
font: var(--type-body-l);
font-size: 0.875rem;
font-weight: var(--font-body-strong-weight);
line-height: 1.5;
padding: 0.5rem 0.75rem;
text-align: left;
vertical-align: middle;
}

.spec-header ~ .spec-header {
display: none;
}

table {
font: var(--type-body-l);
border: 1px solid var(--border-primary);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,13 @@ export default function BrowserCompatibilityTable({
data,
browsers: browserInfo,
locale,
showTableHeadings,
}: {
query: string;
data: bcd.Identifier;
browsers: bcd.Browsers;
locale: string;
showTableHeadings: boolean;
}) {
const location = useLocation();

Expand Down Expand Up @@ -147,8 +149,28 @@ export default function BrowserCompatibilityTable({
return `${url}?${sp.toString()}`;
}

// “tableHeading” holds a BCD feature name. If there’s a “description”
// key in BCD for the feature, we use that description — after stripping
// out any HTML tags from it. Otherwise, if there’s no “description” key,
// we just use the BCD feature name. (These can be different; for
// example, the BCD feature name may be “for_of”, while its description
// is the string “<code>for...of</code>”.
let tableHeading;
if (data && data.__compat) {
tableHeading = data.__compat.description ? (
<h3
dangerouslySetInnerHTML={{
__html: data.__compat.description.replace(/<[^>]+>/g, ""),
}}
/>
) : (
<h3>{name}</h3>
);
}

return (
<BrowserCompatibilityErrorBoundary>
{showTableHeadings ? tableHeading : null}
<BrowserInfoContext.Provider value={browserInfo}>
<a
className="bc-github-link external external-icon"
Expand Down
44 changes: 21 additions & 23 deletions client/src/document/ingredients/spec-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,28 @@ export function SpecificationSection({
{title && !isH3 && <DisplayH2 id={id} title={title} />}
{title && isH3 && <DisplayH3 id={id} title={title} />}

{/*
If we were to output HTML table markup here, then in the case where
we have multiple BCD features from a browser-compat frontmatter
key, we’d end up with multiple tables in the output. So we instead
output a simpler HTML structure for each specification, and use CSS
to push each piece together to make the collective end result look
like a table; client/src/document/index.scss has the CSS styles.
*/}
<div className="spec-header">Specification</div>
{specifications.length > 0 ? (
<table className="standard-table">
<thead>
<tr>
<th scope="col">Specification</th>
</tr>
</thead>
<tbody>
{specifications.map((spec) => (
<tr key={spec.bcdSpecificationURL}>
<td>
<a href={spec.bcdSpecificationURL}>
{spec.title} <br />
{spec.bcdSpecificationURL.includes("#") && (
<small>
# {`${spec.bcdSpecificationURL.split("#")[1]}`}
</small>
)}
</a>
</td>
</tr>
))}
</tbody>
</table>
<div className="specs">
{specifications.map((spec) => (
<p key={spec.bcdSpecificationURL}>
<a href={spec.bcdSpecificationURL}>
{spec.title} <br />
{spec.bcdSpecificationURL.includes("#") && (
<small># {`${spec.bcdSpecificationURL.split("#")[1]}`}</small>
)}
</a>
</p>
))}
</div>
) : (
<NoteCard type="warning">
<h4>No specification found</h4>
Expand Down
Loading