зеркало из https://github.com/github/docs.git
Remove FEATURE_NEXTJS Flag Part 1 (#20176)
* cleanup FEATURE_NEXTJS * fixing some server tests * updating article a for server tests * update h2 to h4 map topic tests * data off on TOCs * updating dropdown article versions links * Update so markdown renders in intros * updating typo and all server tests are now passing * remove nextjs feature flag * head.js tests pass * updating article-version-picker * remove nextjs feature flag browser test * update header.js tests * fix page-titles.js test * fix deprecated-enterprise versions * adding early access * testing * getting childTocItem * fixing table of contents to show child toc items * updated to 2 because the sidebar article also has the same link * remove comment * updating pick * Update TocLandingContext.tsx * update package.json and change className to h4 for h2 * updating with mikes feedback * remove a.active test * React clean up: Delete unnecessary layouts/includes Part 2 (#20143) * Delete unnecessary layouts * setting back tests failing :( * update layouts * delete unnecessary includes * remove github-ae-release-notes and updating layouts * remove a.active test
This commit is contained in:
Родитель
741ca7e481
Коммит
27aa5d92ea
|
@ -11,14 +11,14 @@ import { useTranslation } from './hooks/useTranslation'
|
|||
|
||||
type Props = { children?: React.ReactNode }
|
||||
export const DefaultLayout = (props: Props) => {
|
||||
const { page, error, isHomepageVersion } = useMainContext()
|
||||
const { page, error, isHomepageVersion, currentPathWithoutLanguage } = useMainContext()
|
||||
const { t } = useTranslation('errors')
|
||||
return (
|
||||
<div className="d-lg-flex">
|
||||
<Head>
|
||||
{error === '404' ? (
|
||||
<title>{t('oops')}</title>
|
||||
) : !isHomepageVersion && page.fullTitle ? (
|
||||
) : (!isHomepageVersion && page.fullTitle) || (currentPathWithoutLanguage.includes('enterprise-server') && page.fullTitle) ? (
|
||||
<title>{page.fullTitle}</title>
|
||||
) : null}
|
||||
|
||||
|
|
|
@ -63,6 +63,7 @@ export const LanguagePicker = ({ variant }: Props) => {
|
|||
width: unset;
|
||||
}
|
||||
`}
|
||||
data-testid="language-picker"
|
||||
>
|
||||
<summary>
|
||||
{selectedLang.nativeName || selectedLang.name}
|
||||
|
|
|
@ -55,9 +55,10 @@ export const ArticlePage = () => {
|
|||
)}
|
||||
|
||||
{intro && (
|
||||
<div className="lead-mktg">
|
||||
<p>{intro}</p>
|
||||
</div>
|
||||
<div
|
||||
className="lead-mktg"
|
||||
dangerouslySetInnerHTML={{ __html: intro }}
|
||||
/>
|
||||
)}
|
||||
|
||||
{permissions && (
|
||||
|
|
|
@ -23,6 +23,7 @@ export const ArticleVersionPicker = () => {
|
|||
width: unset;
|
||||
}
|
||||
`}
|
||||
data-testid="article-version-picker"
|
||||
>
|
||||
<summary className="f4 h5-mktg btn-outline-mktg btn-mktg p-2">
|
||||
<span className="d-md-none d-xl-inline-block">{t('article_version')}</span>{' '}
|
||||
|
|
|
@ -41,7 +41,7 @@ export const getArticleContextFromRequest = (req: any): ArticleContextT => {
|
|||
const page = req.context.page
|
||||
return {
|
||||
title: page.titlePlainText,
|
||||
intro: page.introPlainText,
|
||||
intro: page.intro,
|
||||
renderedPage: req.context.renderedPage || '',
|
||||
miniTocItems:
|
||||
(req.context.miniTocItems || []).map((item: any) => {
|
||||
|
|
|
@ -5,6 +5,10 @@ export type TocItem = {
|
|||
fullPath: string
|
||||
title: string
|
||||
intro?: string
|
||||
childTocItems?: Array<{
|
||||
fullPath: string;
|
||||
title: string;
|
||||
}>
|
||||
}
|
||||
export type FeaturedLink = {
|
||||
title: string
|
||||
|
|
|
@ -38,7 +38,7 @@ export const getTocLandingContextFromRequest = (req: any): TocLandingContextT =>
|
|||
introPlainText: req.context.page.introPlainText,
|
||||
isEarlyAccess: req.context.page?.documentType === 'early-access',
|
||||
tocItems: (req.context.genericTocFlat || req.context.genericTocNested || []).map((obj: any) =>
|
||||
pick(obj, ['fullPath', 'title', 'intro'])
|
||||
pick(obj, ['fullPath', 'title', 'intro', 'childTocItems'])
|
||||
),
|
||||
variant: req.context.genericTocFlat ? 'expanded' : 'compact',
|
||||
|
||||
|
|
|
@ -19,11 +19,28 @@ export const TableOfContents = (props: Props) => {
|
|||
return null
|
||||
}
|
||||
|
||||
const { fullPath: href, title, intro } = item
|
||||
const { fullPath: href, title, intro, childTocItems } = item
|
||||
const isActive = router.pathname === href
|
||||
return variant === 'compact' ? (
|
||||
<li key={href} className="f4 my-1">
|
||||
<Link href={href}>{title}</Link>
|
||||
<ul className={cx(variant === 'compact' ? 'list-style-circle pl-5 my-3' : 'list-style-none')}>
|
||||
{(childTocItems || []).map((childItem) => {
|
||||
if (!childItem) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<li key={childItem.fullPath} className="f4 mt-1">
|
||||
<Link
|
||||
href={childItem.fullPath}
|
||||
className="Bump-link--hover no-underline py-1 color-border-primary"
|
||||
>
|
||||
{childItem.title}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</li>
|
||||
) : (
|
||||
<li key={href} className={cx('mb-5', isActive && 'color-auto-gray-4')}>
|
||||
|
@ -31,10 +48,10 @@ export const TableOfContents = (props: Props) => {
|
|||
href={href}
|
||||
className="Bump-link--hover no-underline d-block py-1 border-bottom color-border-primary"
|
||||
>
|
||||
<h4>
|
||||
<h2 className="h4">
|
||||
{title}
|
||||
<span className="Bump-link-symbol">→</span>
|
||||
</h4>
|
||||
</h2>
|
||||
</Link>
|
||||
{intro && <p className="f4 mt-3" dangerouslySetInnerHTML={{ __html: intro }} />}
|
||||
</li>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"FEATURE_TEST_TRUE": true,
|
||||
"FEATURE_TEST_FALSE": false,
|
||||
"FEATURE_NEXTJS": true
|
||||
"FEATURE_TEST_FALSE": false
|
||||
}
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
{% assign maxArticles = 9 %}
|
||||
|
||||
<div class="container-xl px-3 px-md-6 pt-3 pb-2">
|
||||
<!-- When learning tracks aren't present, only guides exist and this heading duplicates the article's title -->
|
||||
{% if page.learningTracks %}
|
||||
<h2 class="mb-3 font-mktg" id="all-guides"><a href="#all-guides">{% data ui.product_sublanding.all_guides %}</a></h2>
|
||||
{% endif %}
|
||||
|
||||
<form class="mt-2 mb-5 d-flex d-flex">
|
||||
<div>
|
||||
<label for="type" class="text-uppercase f6 color-text-secondary d-block">{% data ui.product_sublanding.filters.type %}</label>
|
||||
<select class="form-select js-filter-card-filter-dropdown f4 text-bold border-0 rounded-0 border-top box-shadow-none pl-0 js-filter-card-filter-dropdown" name="type" aria-label="guide types">
|
||||
<option value="">{% data ui.product_sublanding.filters.all %}</option>
|
||||
{% for type in site.data.ui.product_sublanding.guide_types %}
|
||||
<option value="{{ type[0] }}">{{ type[1] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mx-4">
|
||||
<label for="topic" class="text-uppercase f6 color-text-secondary d-block">{% data ui.product_sublanding.filters.topic %}</label>
|
||||
<select class="form-select js-filter-card-filter-dropdown f4 text-bold border-0 rounded-0 border-top box-shadow-none pl-0 js-filter-card-filter-dropdown" name="topics" aria-label="guide topics">
|
||||
<option value="">{% data ui.product_sublanding.filters.all %}</option>
|
||||
{% for topic in page.allTopics %}
|
||||
<option value="{{ topic }}">{{ topic }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
<div class="d-flex flex-wrap mr-0 mr-md-n6 mr-lg-n8">
|
||||
{% for article in page.includeGuides %}
|
||||
{% assign card_display_class = "" %}
|
||||
{% if forloop.index > maxArticles %}
|
||||
{% assign card_display_class = "d-none" %}
|
||||
{% endif %}
|
||||
|
||||
{% capture link_card %}
|
||||
{% link_as_article_card {{ article.href }} %}
|
||||
{% endcapture %}
|
||||
|
||||
{{ link_card | replace: "<display condition>", card_display_class }}
|
||||
{% endfor %}
|
||||
|
||||
{% if page.includeGuides.length > maxArticles %}
|
||||
<button class="col-12 mt-5 text-center text-bold color-text-link btn-link js-filter-card-show-more" data-js-filter-card-max="{{ maxArticles }}">
|
||||
{% data ui.product_sublanding.load_more %}
|
||||
</button>
|
||||
{% endif %}
|
||||
|
||||
<div class="js-filter-card-no-results d-none py-4 text-center color-text-secondary">
|
||||
<h4 class="text-normal">{% data ui.product_sublanding.no_result %}</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,20 +0,0 @@
|
|||
{% if page.permalinks and page.permalinks.length > 1 %}
|
||||
<details class="dropdown-withArrow d-inline-block details details-reset mb-1 position-relative close-when-clicked-outside article-versions">
|
||||
<summary class="f4 h5-mktg btn-outline-mktg btn-mktg p-2">
|
||||
<!-- GitHub.com, Enterprise Server 2.16, etc -->
|
||||
<span class="d-md-none d-xl-inline-block">{% data ui.pages.article_version %}</span> {{ allVersions[currentVersion].versionTitle }}
|
||||
<svg class="arrow ml-1" width="14px" height="8px" viewBox="0 0 14 8" xml:space="preserve" fill="none" stroke="currentColor"><path d="M1,1l6.2,6L13,1"></path></svg>
|
||||
</summary>
|
||||
|
||||
<div class="nav-dropdown position-absolute color-bg-primary rounded-1 px-4 py-3 top-7 color-shadow-large" style="z-index: 6; width: 210px;">
|
||||
{% for permalink in page.permalinks %}
|
||||
<a
|
||||
href="{{ permalink.href }}"
|
||||
class="d-block py-2 {% if currentPath == permalink.href %}link-blue active{% else %}Link--primary no-underline{% endif %}"
|
||||
>
|
||||
{{ allVersions[permalink.pageVersion].versionTitle }}</a>
|
||||
{% endfor %}
|
||||
{% include all-enterprise-releases-link %}
|
||||
</div>
|
||||
</details>
|
||||
{% endif %}
|
|
@ -1,88 +0,0 @@
|
|||
<div class="container-xl px-3 px-md-6 my-4 my-lg-4 d-xl-flex">
|
||||
<article class="markdown-body width-full">
|
||||
<div class="d-lg-flex flex-justify-between">
|
||||
<div class="d-block d-lg-none">{% include article-version-switcher %}</div>
|
||||
<div class="d-flex flex-items-center breadcrumbs-wrapper">
|
||||
{% include breadcrumbs %}
|
||||
</div>
|
||||
<div class="d-none d-lg-block">{% include article-version-switcher %}</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-2 article-grid-container">
|
||||
|
||||
<div class="article-grid-head">
|
||||
<div class="d-flex flex-items-baseline flex-justify-between mt-3">
|
||||
<h1 class="border-bottom-0">{{ page.title }}</h1>
|
||||
<div class="d-none d-lg-block ml-2">
|
||||
<button class="btn-link Link--secondary js-print tooltipped tooltipped-n" aria-label="Print this article">
|
||||
{% include printer-icon %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if page.contributor %}
|
||||
<div class="contributor-callout border rounded-1 mb-4 p-3 color-border-info color-bg-info f5">
|
||||
<p><span class="mr-2">{% octicon "info" %}</span>{% data ui.pages.contributor_callout %} <a href="{{ page.contributor.URL }}">{{ page.contributor.name }}</a>.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if page.intro %}
|
||||
<div class="lead-mktg">{{ page.intro }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if page.permissions %}
|
||||
<div class="permissions-statement">{{ page.permissions }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if page.includesPlatformSpecificContent %}
|
||||
<nav class="UnderlineNav my-3"
|
||||
{%- if page.defaultPlatform %} data-default-platform="{{ page.defaultPlatform }}"{% endif %}>
|
||||
<div class="UnderlineNav-body">
|
||||
<a href="#" class="UnderlineNav-item platform-switcher" data-platform="mac">Mac</a>
|
||||
<a href="#" class="UnderlineNav-item platform-switcher" data-platform="windows">Windows</a>
|
||||
<a href="#" class="UnderlineNav-item platform-switcher" data-platform="linux">Linux</a>
|
||||
</div>
|
||||
</nav>
|
||||
{% endif %}
|
||||
|
||||
{% if page.product %}
|
||||
<div class="product-callout border rounded-1 mb-4 p-3 color-border-success color-bg-success">
|
||||
{{ page.product }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="article-grid-toc border-bottom border-xl-0 pb-4 mb-5 pb-xl-0 mb-xl-0">
|
||||
<div class="article-grid-toc-content">
|
||||
{% if miniTocItems.size > 1 %}
|
||||
<h2 id="in-this-article" class="f5 mb-2"><a class="Link--primary" href="#in-this-article">{% data ui.pages.miniToc %}</a></h2>
|
||||
<ul class="list-style-none pl-0 f5 mb-0">
|
||||
{% for item in miniTocItems %}
|
||||
<li class="ml-{{ item.indentationLevel | times: 3 }} {{ item.platform }} mb-2 lh-condensed">{{ item.contents }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div id="article-contents" class="article-grid-body">
|
||||
{% if featuredLinks.gettingStarted and featuredLinks.popular %}
|
||||
{% include featured-links %}
|
||||
{% endif %}
|
||||
{{ renderedPage }}
|
||||
|
||||
{% if genericTocFlat %}
|
||||
{% include generic-toc-flat %}
|
||||
{% endif %}
|
||||
|
||||
{% if genericTocNested %}
|
||||
{% include generic-toc-nested %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-block mt-4 markdown-body">
|
||||
{% if currentLearningTrack and currentLearningTrack.trackName %}
|
||||
{% include learning-track-nav %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
|
@ -1,9 +0,0 @@
|
|||
<nav class="breadcrumbs f5" aria-label="Breadcrumb">
|
||||
{%- for breadcrumb in breadcrumbs -%}
|
||||
{%- if breadcrumb.href == '' -%}
|
||||
<span title="{{ breadcrumb.documentType }}: {{ breadcrumb.title }}">{{ breadcrumb.title }}</span>
|
||||
{%- else -%}
|
||||
<a title="{{ breadcrumb.documentType }}: {{ breadcrumb.title }}" href="{{ breadcrumb.href }}" class="d-inline-block {% if breadcrumb.href == currentPath %}color-text-tertiary{% endif %}">{{ breadcrumb.title }}</a>
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
</nav>
|
|
@ -1,40 +0,0 @@
|
|||
{% for categoryPage in currentProductTree.childPages %}
|
||||
{% if categoryPage.href == currentPath %}{% assign currentCategory = categoryPage %}{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if currentCategory.page.shortTitle and currentCategory.page.shortTitle != '' %}{% assign currentCategoryTitle = currentCategory.page.shortTitle %}{% else %}{% assign currentCategoryTitle = currentCategory.page.title %}{% endif %}
|
||||
|
||||
{% assign maxArticles = 10 %}
|
||||
|
||||
<div class="py-6 all-articles-list">
|
||||
<h2 class="font-mktg mb-4" id="all-docs"><a href="#all-docs">{{ currentCategoryTitle }} docs</a></h2>
|
||||
|
||||
<div class="d-flex gutter flex-wrap">
|
||||
{% for childPage in currentCategory.childPages %}
|
||||
{% unless childPage.page.hidden %}
|
||||
<div class="col-12 col-lg-4 mb-6 height-full">
|
||||
<h4 class="mb-3"><a href="{{ childPage.href }}">{{ childPage.page.title }}</a></h4>
|
||||
<ul class="list-style-none">
|
||||
{% for grandchildPage in childPage.childPages %}
|
||||
<li class="mb-3 {% if forloop.index > maxArticles %}d-none{% endif %}">
|
||||
<a href="{{ grandchildPage.href }}">
|
||||
{{ grandchildPage.page.title }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% assign numArticles = childPage.childPages | obj_size %}
|
||||
{% if numArticles > maxArticles %}
|
||||
<button class="js-all-articles-show-more btn-link Link--secondary">Show {{ numArticles | minus: maxArticles }} more {% octicon "chevron-up" class="v-align-text-bottom" %}</button>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endunless %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% comment %}
|
||||
<small class="color-text-secondary d-inline-block">
|
||||
• {{ article.childArticles | obj_size }} articles
|
||||
</small>
|
||||
{% endcomment %}
|
|
@ -1,20 +0,0 @@
|
|||
<div class="col-12 col-xl-4 col-lg-6 mb-4 js-filter-card {% if forloop.index0 > 5 %}d-none{% endif %}" data-tags="{{ example.tags | join: ', ' }}" data-title="{{ example.title }}" data-description="{{ example.description }}">
|
||||
<a
|
||||
class="Box d-flex flex-column flex-justify-between height-full color-shadow-medium hover-shadow-large no-underline color-text-primary"
|
||||
href="https://github.com/{{ example.href }}"
|
||||
>
|
||||
<div class="p-4">
|
||||
<h4>{{ example.title }}</h4>
|
||||
<p class="mt-2 mb-4 color-text-tertiary">{{ example.description }}</p>
|
||||
<div class="d-flex flex-wrap">
|
||||
{% for tag in example.tags %}
|
||||
<span class="IssueLabel color-text-inverse color-bg-info-inverse mr-2 mb-1">{{ tag }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<footer class="border-top p-4 color-text-secondary d-flex flex-items-center">
|
||||
{% octicon "repo" class="flex-shrink-0" %}
|
||||
<span class="ml-2 text-mono text-small color-text-link">{{ example.href | truncate: 43 }}</span>
|
||||
</footer>
|
||||
</a>
|
||||
</div>
|
|
@ -1,22 +0,0 @@
|
|||
<div class="my-6 pt-6">
|
||||
<h2 class="mb-2 font-mktg h1" id="code-examples"><a href="#code-examples">{% data ui.product_landing.code_examples %}</a></h2>
|
||||
|
||||
<div class="pr-lg-3 mb-5 mt-3">
|
||||
<input class="js-filter-card-filter input-lg py-2 px-3 col-12 col-lg-8 form-control" placeholder="{% data ui.product_landing.search_code_examples %}" type="search" autocomplete="off" aria-label="Search code examples"/>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-wrap gutter">
|
||||
{% render code-example-card for productCodeExamples as example %}
|
||||
</div>
|
||||
|
||||
{% if productCodeExamples.length > 6 %}
|
||||
<button class="js-filter-card-show-more btn btn-outline float-right" data-js-filter-card-max="6">{% data ui.product_landing.show_more %} {% octicon "arrow-right" %}</button>
|
||||
{% endif %}
|
||||
|
||||
<div class="js-filter-card-no-results d-none py-4 text-center color-text-secondary font-mktg">
|
||||
<div class="mb-3">{% octicon "search" width="24" %}</div>
|
||||
<h3 class="text-normal">{% data ui.product_landing.sorry %} <strong class="js-filter-card-value"></strong></h3>
|
||||
<p class="my-3 f4">{% data ui.product_landing.no_result %}<br>{% data ui.product_landing.try_another %}</p>
|
||||
<a href="https://github.com/github/docs/blob/main/data/variables/actions_code_examples.yml">{% data ui.product_landing.learn %} {% octicon "arrow-right" %}</a>
|
||||
</div>
|
||||
</div>
|
|
@ -1,16 +0,0 @@
|
|||
<div class="my-6 pt-6">
|
||||
<h2 class="mb-2 font-mktg h1" id="community-examples"><a href="#community-examples">{% data ui.product_landing.communities_using_discussions %}</a></h2>
|
||||
|
||||
<div class="d-flex flex-wrap gutter">
|
||||
{% render discussions-community-card for productCommunityExamples as example %}
|
||||
</div>
|
||||
{% if productCommunityExamples.length > 6 %}
|
||||
<button class="js-filter-card-show-more btn btn-outline float-right" data-js-filter-card-max="6">{% data ui.product_landing.show_more %} {% octicon "arrow-right" %}</button>
|
||||
{% endif %}
|
||||
<div class="js-filter-card-no-results d-none py-4 text-center color-text-secondary font-mktg">
|
||||
<div class="mb-3">{% octicon "search" width="24" %}</div>
|
||||
<h3 class="text-normal">{% data ui.product_landing.sorry %} <strong class="js-filter-card-value"></strong></h3>
|
||||
<p class="my-3 f4">{% data ui.product_landing.no_example %} <br>{% data ui.product_landing.try_another %}</p>
|
||||
<a href="https://github.com/github/docs/blob/main/data/variables/discussions_community_examples.yml">{% data ui.product_landing.add_your_community %} {% octicon "arrow-right" %}</a>
|
||||
</div>
|
||||
</div>
|
|
@ -1,17 +0,0 @@
|
|||
{% if currentVersion contains enterpriseServerReleases.oldestSupported %}
|
||||
<div class="deprecation-banner border rounded-1 mb-2 color-bg-warning p-3 color-border-warning f5">
|
||||
<p>
|
||||
<b>
|
||||
<span>
|
||||
{% if enterpriseServerReleases.isOldestReleaseDeprecated %}
|
||||
{% data reusables.enterprise_deprecation.version_was_deprecated %}
|
||||
{% else %}
|
||||
{% data reusables.enterprise_deprecation.version_will_be_deprecated %}
|
||||
{% endif %}
|
||||
</span>
|
||||
<span data-date="{{ enterpriseServerReleases.nextDeprecationDate }}" data-format="%B %d, %Y" title="{{ enterpriseServerReleases.nextDeprecationDate }}">{{ enterpriseServerReleases.nextDeprecationDate }}</span>.
|
||||
</b>
|
||||
{% data reusables.enterprise_deprecation.deprecation_details %}
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
|
@ -1,14 +0,0 @@
|
|||
<div class="col-12 col-xl-4 col-lg-6 mb-4 js-filter-card {% if forloop.index0 > 5 %}d-none{% endif %}" data-repo="{{ example.repo }}" data-description="{{ example.description }}">
|
||||
<a
|
||||
class="Box d-flex height-full color-shadow-medium hover-shadow-large no-underline color-text-primary p-4"
|
||||
href="https://github.com/{{ example.repo }}/discussions"
|
||||
>
|
||||
<div class="flex-shrink-0 mr-3">
|
||||
<img src="https://github.com/{{ example.repo | split: '/' | first }}.png" alt="{{ example.repo }}" class="avatar avatar-8">
|
||||
</div>
|
||||
<div class="flex-auto">
|
||||
<h4>{{ example.repo }}</h4>
|
||||
<p class="mt-1 color-text-tertiary">{{ example.description }}</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
|
@ -1,156 +0,0 @@
|
|||
{% assign product = siteTree[currentLanguage][currentVersion].products[currentProduct] %}
|
||||
{% assign currentVersionObject = allVersions[currentVersion] %}
|
||||
|
||||
<div class="d-flex">
|
||||
<article class="min-width-0 flex-1">
|
||||
<div class="d-flex flex-items-center flex-justify-between color-bg-primary text-bold px-5 py-2">
|
||||
{% if prevRelease %}
|
||||
<a
|
||||
class="btn btn-outline"
|
||||
href="/{{ currentLanguage }}/{{ currentVersionObject.plan }}@{{ prevRelease }}/{{ currentProduct }}/release-notes">
|
||||
{% octicon "chevron-left" %} {{ prevRelease }}
|
||||
</a>
|
||||
{% else %}
|
||||
<div></div>
|
||||
{% endif %}
|
||||
|
||||
<h1 class="f4 py-3 m-0">{{ currentVersionObject.planTitle }} {{ currentVersionObject.currentRelease }} release notes</h1>
|
||||
|
||||
{% if nextRelease %}
|
||||
<a
|
||||
class="btn btn-outline"
|
||||
href="/{{ currentLanguage }}/{{ currentVersionObject.plan }}@{{ nextRelease }}/{{ currentProduct }}/release-notes">
|
||||
{{ nextRelease }} {% octicon "chevron-right" %}
|
||||
</a>
|
||||
{% else %}
|
||||
<div></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="markdown-body">
|
||||
{% for patch in releaseNotes %}
|
||||
<div class="js-release-notes-patch mb-10 color-bg-secondary pb-6 border-bottom border-top" data-version="{{ patch.version }}" id="{{ patch.version }}">
|
||||
<header style="z-index: 1;" class="container-xl position-sticky top-0 color-bg-secondary border-bottom px-3 pt-4 pb-2 js-release-notes-patch-heading" data-version="{{ patch.version }}">
|
||||
<div class="d-flex flex-items-center">
|
||||
<h2 class="border-bottom-0 m-0 p-0">
|
||||
{{ currentVersionObject.versionTitle }}.{{ patch.patchVersion }}
|
||||
</h2>
|
||||
|
||||
{% if patch.release_candidate %}
|
||||
<span class="IssueLabel color-bg-warning-inverse color-text-inverse ml-3" style="white-space: pre">Release Candidate</span>
|
||||
{% endif %}
|
||||
|
||||
{% if currentVersionObject.plan == "enterprise-server" %}
|
||||
<a href="https://enterprise.github.com/releases/{{ patch.downloadVersion }}/download" class="ml-3 text-small text-bold">
|
||||
Download
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
<button class="js-print btn-link ml-3 text-small text-bold">
|
||||
Print
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p class="color-text-secondary mt-1">{{ patch.date | date: "%B %d, %Y" }}</p>
|
||||
|
||||
{% if patch.version != latestPatch and currentVersionObject.currentRelease == latestRelease %}
|
||||
<p class="color-text-secondary mt-1">{% data ui.header.notices.ghes_release_notes_upgrade_patch_only %} {% data ui.header.notices.release_notes_use_latest %}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if patch.version == latestPatch and currentVersionObject.currentRelease != latestRelease %}
|
||||
<p class="color-text-secondary mt-1">{% data ui.header.notices.ghes_release_notes_upgrade_release_only %} {% data ui.header.notices.release_notes_use_latest %}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if patch.version != latestPatch and currentVersionObject.currentRelease != latestRelease %}
|
||||
<p class="color-text-secondary mt-1">{% data ui.header.notices.ghes_release_notes_upgrade_patch_and_release %} {% data ui.header.notices.release_notes_use_latest %}</p>
|
||||
{% endif %}
|
||||
</header>
|
||||
|
||||
<div class="container-xl px-3">
|
||||
<div class="mt-3">{{ patch.intro }}</div>
|
||||
|
||||
{% for section in patch.sections %}
|
||||
<div class="release-notes-section-{{ section[0] }} py-6 d-block d-xl-flex gutter-xl flex-items-baseline {% unless forloop.last %}border-bottom{% endunless %}">
|
||||
<div class="col-12 col-xl-3 mb-5">
|
||||
{% include release-notes-category-label %}
|
||||
</div>
|
||||
|
||||
<ul class="col-12 col-xl-9 release-notes-list">
|
||||
{% for note in section[1] %}
|
||||
<li class="release-notes-list-item {% if note.heading %}list-style-none{% endif %}">
|
||||
{% if note.heading %}
|
||||
{% assign slug = note.heading | slugify %}
|
||||
|
||||
<h4 id="{{ slug }}" class="release-notes-section-heading text-uppercase text-bold">
|
||||
<a href="#{{ slug }}" class="text-inherit">{{ note.heading }}</a>
|
||||
</h4>
|
||||
|
||||
<ul class="pl-0 pb-4 mt-2 release-notes-list">
|
||||
{% for subNote in note.notes %}
|
||||
<li class="release-notes-list-item">{{ subNote }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
{{ note }}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<aside
|
||||
class="markdown-body position-sticky top-0 d-none d-md-block border-left no-print color-bg-primary flex-shrink-0"
|
||||
style="width: 260px; height: 100vh;"
|
||||
>
|
||||
<nav class="height-full overflow-auto">
|
||||
<ul class="list-style-none pl-0 text-bold">
|
||||
{% for release in releases %}
|
||||
{% capture releaseLink %}/{{ currentLanguage }}/{{ currentVersionObject.plan }}@{{ release.version }}/{{ currentProduct }}/release-notes{% endcapture %}
|
||||
<li class="border-bottom">
|
||||
{% if release.patches %}
|
||||
{% if release.version == currentVersionObject.currentRelease %}
|
||||
<details class="my-0 details-reset release-notes-version-picker" aria-current="page" open>
|
||||
<summary class="px-3 py-4 my-0 d-flex flex-items-center flex-justify-between">
|
||||
{{ release.version }}
|
||||
<div class="d-flex">
|
||||
<span class="color-text-tertiary text-mono text-small text-normal mr-1">{{ release.patches | size }} releases</span>
|
||||
{% octicon "chevron-down" %}
|
||||
</div>
|
||||
</summary>
|
||||
<ul class="color-bg-tertiary border-top list-style-none py-4 px-0 my-0">
|
||||
{% for patch in release.patches %}
|
||||
<li class="js-release-notes-patch-link px-3 my-0 py-1" data-version="{{ patch.version }}">
|
||||
<a
|
||||
href="{{ releaseLink }}#{{ patch.version }}"
|
||||
class="d-flex flex-items-center flex-justify-between">
|
||||
{{ patch.version }}
|
||||
<span class="color-text-tertiary text-mono text-small text-normal">{{ patch.date | date: "%B %d, %Y" }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</details>
|
||||
{% else %}
|
||||
<a class="px-3 py-4 my-0 d-flex flex-items-center flex-justify-between" href="{{ releaseLink }}">
|
||||
{{ release.version }}
|
||||
<span class="color-text-tertiary text-mono text-small text-normal mr-1">{{ release.patches | size }} releases</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<a href="{{ releaseLink }}" class="Link--primary no-underline px-3 py-4 my-0 d-flex flex-items-center flex-justify-between">
|
||||
{{ release.version }}
|
||||
{% octicon "link-external" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</nav>
|
||||
</aside>
|
||||
</div>
|
|
@ -1,11 +0,0 @@
|
|||
{% include breadcrumbs %}
|
||||
|
||||
<div class="graphql-container">
|
||||
{% if AIRGAP %}
|
||||
<p>GraphQL explorer is not available on this environment.</p>
|
||||
{% else %}
|
||||
<iframe id="graphiql" class="graphql-explorer" scrolling="no" src="{{ graphql.explorerUrl }}" allowfullscreen>
|
||||
<p>You must have iframes enabled to use this feature.</p>
|
||||
</iframe>
|
||||
{% endif %}
|
||||
</div>
|
|
@ -1,3 +0,0 @@
|
|||
<a class="link-with-intro Bump-link--hover no-underline d-block py-2" href="{{ link.href }}">
|
||||
<h4 class="link-with-intro-title Link--primary">{{ link.title }}<span class="Bump-link-symbol">→</span></h4>
|
||||
</a>
|
|
@ -1,4 +0,0 @@
|
|||
<a class="link-with-intro Bump-link--hover no-underline d-block py-2" href="{{ link.href }}">
|
||||
<h4 class="link-with-intro-title Link--primary">{{ link.title }}<span class="Bump-link-symbol">→</span></h4>
|
||||
<p class="link-with-intro-intro color-text-secondary">{{ link.intro }}</p>
|
||||
</a>
|
|
@ -1,27 +0,0 @@
|
|||
<div class="featured-links container-xl">
|
||||
<div class="gutter gutter-xl-spacious clearfix">
|
||||
|
||||
<!-- Getting Started articles -->
|
||||
<div class="col-12 col-lg-6 float-left">
|
||||
<div class="featured-links-heading pb-4">
|
||||
<h3 class="f5 text-normal text-mono underline-dashed color-text-secondary">{% data ui.toc.getting_started %}</h3>
|
||||
</div>
|
||||
|
||||
{% for link in featuredLinks.gettingStarted %}
|
||||
{% include featured-link %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- Popular articles -->
|
||||
<div class="col-12 col-lg-6 float-left">
|
||||
<div class="featured-links-heading pb-4">
|
||||
<h3 class="f5 text-normal text-mono underline-dashed color-text-secondary">{% data ui.toc.popular %}</h3>
|
||||
</div>
|
||||
|
||||
{% for link in featuredLinks.popular %}
|
||||
{% include featured-link %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
|
@ -1,8 +0,0 @@
|
|||
{% for tocItem in genericTocFlat %}
|
||||
|
||||
{% assign title = tocItem.title %}
|
||||
{% assign fullPath = tocItem.fullPath %}
|
||||
{% assign intro = tocItem.intro %}
|
||||
{% include liquid-tags/link-with-intro %}
|
||||
|
||||
{% endfor %}
|
|
@ -1,19 +0,0 @@
|
|||
<ul>
|
||||
{% for tocItem in genericTocNested %}
|
||||
|
||||
{% assign title = tocItem.title %}
|
||||
{% assign fullPath = tocItem.fullPath %}
|
||||
|
||||
<li>{% include liquid-tags/link %}
|
||||
{% if tocItem.childTocItems %}
|
||||
<ul>
|
||||
{% for childItem in tocItem.childTocItems %}
|
||||
{% assign title = childItem.title %}
|
||||
{% assign fullPath = childItem.fullPath %}
|
||||
<li>{% include liquid-tags/link %}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
|
@ -1,104 +0,0 @@
|
|||
{% assign product = siteTree[currentLanguage][currentVersion].products[currentProduct] %}
|
||||
{% assign currentVersionObject = allVersions[currentVersion] %}
|
||||
|
||||
<div class="d-flex">
|
||||
<article class="min-width-0 flex-1">
|
||||
<div class="d-flex flex-items-center flex-justify-between color-bg-primary px-5 py-2">
|
||||
<div></div>
|
||||
<h1 class="f4 py-3 m-0">{{ currentVersionObject.planTitle }} release notes</h1>
|
||||
<div></div>
|
||||
</div>
|
||||
|
||||
<div class="markdown-body">
|
||||
{% for patch in releaseNotes %}
|
||||
<div class="js-release-notes-patch mb-10 color-bg-secondary pb-6 border-bottom border-top" data-version="{{ patch.date }}" id="{{ patch.date }}">
|
||||
<header style="z-index: 1;" class="container-xl position-sticky top-0 color-bg-secondary border-bottom px-3 pt-4 pb-2 js-release-notes-patch-heading" data-version="{{ patch.date }}">
|
||||
<div class="d-flex flex-items-center">
|
||||
<h2 class="border-bottom-0 m-0 p-0">
|
||||
{{ patch.title }}
|
||||
</h2>
|
||||
|
||||
{% if patch.release_candidate %}
|
||||
<span class="IssueLabel color-bg-warning-inverse color-text-inverse ml-3" style="white-space: pre">Release Candidate</span>
|
||||
{% endif %}
|
||||
|
||||
<button class="js-print btn-link ml-3 text-small text-bold">
|
||||
Print
|
||||
</button>
|
||||
</div>
|
||||
{% if patch.currentWeek %}
|
||||
{% assign bannerText = site.data.ui.release_notes.banner_text_current %}
|
||||
{% else %}
|
||||
{% assign bannerText = site.data.ui.release_notes.banner_text_past | append: " " | append: patch.friendlyDate | append: "." %}
|
||||
{% endif %}
|
||||
<p class="color-text-secondary mt-1">{{ patch.friendlyDate }} - {{ bannerText }}</p>
|
||||
</header>
|
||||
|
||||
<div class="container-xl px-3">
|
||||
<div class="mt-3">{{ patch.intro }}</div>
|
||||
|
||||
{% for section in patch.sections %}
|
||||
<div class="release-notes-section-{{ section[0] }} py-6 mx-6 d-block d-xl-flex gutter-xl flex-items-baseline {% unless forloop.last %}border-bottom{% endunless %}">
|
||||
<ul class="col-12 release-notes-list">
|
||||
{% for note in section[1] %}
|
||||
<li class="release-notes-list-item {% if note.heading %}list-style-none{% endif %}">
|
||||
{% if note.heading %}
|
||||
{% assign slug = note.heading | slugify %}
|
||||
|
||||
<h4 id="{{ slug }}" class="release-notes-section-heading text-uppercase text-bold">
|
||||
<a href="#{{ slug }}" class="text-inherit">{{ note.heading }}</a>
|
||||
</h4>
|
||||
|
||||
<ul class="pl-0 pb-4 mt-2 release-notes-list">
|
||||
{% for subNote in note.notes %}
|
||||
<li class="release-notes-list-item">{{ subNote }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
{{ note }}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<aside
|
||||
class="markdown-body position-sticky top-0 d-none d-md-block border-left no-print color-bg-primary flex-shrink-0"
|
||||
style="width: 260px; height: 100vh;"
|
||||
>
|
||||
<nav class="height-full overflow-auto">
|
||||
<ul class="list-style-none pl-0 text-bold">
|
||||
{% for release in releases %}
|
||||
<li class="border-bottom">
|
||||
<details class="my-0 details-reset release-notes-version-picker" aria-current="page" open>
|
||||
<summary class="px-3 py-4 my-0 d-flex flex-items-center flex-justify-between">
|
||||
{{ release.version }}
|
||||
<div class="d-flex">
|
||||
<span class="color-text-tertiary text-mono text-small text-normal mr-1">{{ release.patches | size }} releases</span>
|
||||
{% octicon "chevron-down" %}
|
||||
</div>
|
||||
</summary>
|
||||
<ul class="color-bg-tertiary border-top list-style-none py-4 px-0 my-0">
|
||||
{% for patch in release.patches %}
|
||||
<li class="js-release-notes-patch-link px-3 my-0 py-1" data-version="{{ patch.date }}">
|
||||
<a
|
||||
href="#{{ patch.date }}"
|
||||
class="d-flex flex-items-center flex-justify-between">
|
||||
{{ patch.friendlyDate }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</nav>
|
||||
</aside>
|
||||
</div>
|
|
@ -1,40 +0,0 @@
|
|||
{% if guide.page.authors %}
|
||||
{% assign authors = guide.page.authors %}
|
||||
{% else %}
|
||||
{% assign authors = 'GitHub' | split: ' ' %}
|
||||
{% endif %}
|
||||
{% assign authorsString = authors | join: ", @" %}
|
||||
|
||||
<div class="col-lg-4 col-12 mb-3">
|
||||
<a class="Box color-shadow-medium height-full d-block hover-shadow-large no-underline color-text-primary p-5" href="{{ guide.href }}">
|
||||
<h2>{{ guide.title }}</h2>
|
||||
<p class="mt-2 mb-4 color-text-tertiary">{{ guide.intro }}</p>
|
||||
|
||||
<footer class="d-flex">
|
||||
<div class="mr-1">
|
||||
{% if authors.length == 1 %}
|
||||
<img class="avatar avatar-2 circle mr-1" src="https://github.com/{{ authors[0] }}.png" alt="@{{ authors[0] }}" />
|
||||
{% else %}
|
||||
<div class="AvatarStack AvatarStack--three-plus">
|
||||
<div
|
||||
class="AvatarStack-body tooltipped tooltipped-se tooltipped-align-left-1"
|
||||
aria-label="@{{ authorsString }}"
|
||||
>
|
||||
{% for author in authors %}
|
||||
<img
|
||||
class="avatar circle"
|
||||
alt="@{{ author }}"
|
||||
src="https://github.com/{{ author }}.png"
|
||||
/>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@{{ authorsString }}
|
||||
</div>
|
||||
</footer>
|
||||
</a>
|
||||
</div>
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -1,18 +0,0 @@
|
|||
<div class="py-3 px-4 rounded color-bg-primary border-gradient--purple-pink d-flex flex-justify-between learning-track-nav">
|
||||
{% assign track = currentLearningTrack %}
|
||||
|
||||
<span class="d-flex flex-column">
|
||||
{% if track.prevGuide %}
|
||||
<span class="f6 color-text-secondary">{% data ui.learning_track_nav.prevGuide %}</span>
|
||||
<a href="{{track.prevGuide.href}}?learn={{track.trackName}}" class="text-bold color-text-secondary">{{track.prevGuide.title}}</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
|
||||
<span class="d-flex flex-column flex-items-end">
|
||||
{% if track.nextGuide %}
|
||||
<span class="f6 color-text-secondary">{% data ui.learning_track_nav.nextGuide %}</span>
|
||||
<a href="{{track.nextGuide.href}}?learn={{track.trackName}}" class="text-bold color-text-secondary text-right">{{track.nextGuide.title}}</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
|
||||
</div>
|
|
@ -1,2 +0,0 @@
|
|||
<!-- From https://heroicons.dev/ -->
|
||||
<svg fill="none" height="18" width="18" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" stroke="currentColor"><path d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z"></path></svg>
|
До Ширина: | Высота: | Размер: 379 B |
|
@ -1,52 +0,0 @@
|
|||
{% assign maxArticles = 10 %}
|
||||
|
||||
{% if currentProductTree.renderedShortTitle %}{% assign productTitle = currentProductTree.renderedShortTitle %}{% else %}{% assign productTitle = currentProductTree.renderedFullTitle %}{% endif %}
|
||||
|
||||
<div class="py-6 all-articles-list">
|
||||
<h2 class="font-mktg mb-4" id="all-docs"><a href="#all-docs">All {{ productTitle }} docs</a></h2>
|
||||
|
||||
<div class="d-flex gutter flex-wrap">
|
||||
{% for childPage in currentProductTree.childPages %}
|
||||
{% if childPage.page.documentType == "article" %}{% assign standaloneCategory = true %}{% else %}{% assign standaloneCategory = false %}{% endif %}
|
||||
{% unless standaloneCategory %}
|
||||
<div class="col-12 col-lg-4 mb-6 height-full">
|
||||
<h4 class="mb-3"><a href="{{ childPage.href }}">{{ childPage.renderedFullTitle }}</a></h4>
|
||||
|
||||
{% if childPage.childPages and childPage.childPages[0].page.documentType == "mapTopic" %}
|
||||
<ul class="list-style-none">
|
||||
{% for grandchildPage in childPage.childPages %}
|
||||
{% unless grandchildPage.page.hidden %}
|
||||
{% assign numArticles = childPage.childPages | obj_size %}
|
||||
<li class="mb-3 {% if forloop.index > maxArticles %}d-none{% endif %}">
|
||||
<a href="{{ grandchildPage.href }}">
|
||||
{{ grandchildPage.renderedFullTitle }}
|
||||
</a>
|
||||
</li>
|
||||
{% if numArticles > maxArticles %}
|
||||
<button class="js-all-articles-show-more btn-link Link--secondary">Show {{ numArticles | minus: maxArticles }} more {% octicon "chevron-up" class="v-align-text-bottom" %}</button>
|
||||
{% endif %}
|
||||
{% endunless %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<ul class="list-style-none">
|
||||
{% assign numArticles = childPage.childPages | obj_size %}
|
||||
{% for grandchildPage in childPage.childPages %}
|
||||
<li class="mb-3 {% if forloop.index > maxArticles %}d-none{% endif %}"><a href="{{ grandchildPage.href }}">{{ grandchildPage.renderedFullTitle }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% if numArticles > maxArticles %}
|
||||
<button class="js-all-articles-show-more btn-link Link--secondary">Show {{ numArticles | minus: maxArticles }} more {% octicon "chevron-up" class="v-align-text-bottom" %}</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endunless %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% comment %}
|
||||
<small class="color-text-secondary d-inline-block">
|
||||
• {{ article.childArticles | obj_size }} articles
|
||||
</small>
|
||||
{% endcomment %}
|
|
@ -1,27 +0,0 @@
|
|||
<div class="bg-guides-gradient py-6">
|
||||
<div class="container-xl px-3 px-md-6 my-6">
|
||||
<h2 class="font-mktg h1 mb-2" id="supported-releases"><a href="#supported-releases">{% data ui.product_landing.supported_releases %}</a></h2>
|
||||
|
||||
<div class="d-lg-flex gutter-lg flex-items-stretch">
|
||||
{% for release in releases %}
|
||||
{% assign releaseNumber = release.version %}
|
||||
{% if enterpriseServerReleases.supported contains releaseNumber %}
|
||||
{% assign releaseVersion = 'enterprise-server@' | append: releaseNumber %}
|
||||
{% assign latestPatch = release.patches[0] %}
|
||||
{% assign firstPreviousVersion = 'enterprise-server@' | append: release.firstPreviousRelease %}
|
||||
{% assign secondPreviousVersion = 'enterprise-server@' | append: release.secondPreviousRelease %}
|
||||
<div class="col-lg-4 col-12 mb-3">
|
||||
<div class="Box color-shadow-medium height-full d-block hover-shadow-large no-underline color-text-primary p-5">
|
||||
<h2>{{ allVersions[releaseVersion].versionTitle }}</h2>
|
||||
<p class="mt-2 mb-4 color-text-tertiary">{% octicon "list-unordered" %} <a href="/{{ currentLanguage }}/{{ releaseVersion }}/admin/release-notes#{{ latestPatch.version }}">{% data ui.product_landing.release_notes_for %} {{ latestPatch.version }}</a> ({{ latestPatch.date }})</p>
|
||||
<p class="mt-2 mb-4 color-text-tertiary">{% octicon "arrow-up" %} {% data ui.product_landing.upgrade_from %} <a href="/{{ currentLanguage }}/{{ firstPreviousVersion }}/admin/enterprise-management/upgrading-github-enterprise-server">{{ release.firstPreviousRelease }}</a> or <a href="/{{ currentLanguage }}/{{ secondPreviousVersion }}/admin/enterprise-management/upgrading-github-enterprise-server">{{ release.secondPreviousRelease }}</a></p>
|
||||
<p class="mt-2 mb-4 color-text-tertiary">{% octicon "file" %} <a href="/{{ currentLanguage }}/{{ releaseVersion }}">{% data ui.product_landing.browse_all_docs %}</a></p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<a href="{{ currentPath }}/release-notes" class="btn btn-outline float-right">{% data ui.product_landing.explore_release_notes %} {% octicon "arrow-right" %}</a>
|
||||
</div>
|
||||
</div>
|
|
@ -1,20 +0,0 @@
|
|||
{% case section[0] %}
|
||||
{% when "features" %}
|
||||
{% assign text = "Features" %}
|
||||
{% when "bugs" %}
|
||||
{% assign text = "Bug fixes" %}
|
||||
{% when "known_issues" %}
|
||||
{% assign text = "Known issues" %}
|
||||
{% when "security_fixes" %}
|
||||
{% assign text = "Security fixes" %}
|
||||
{% when "changes" %}
|
||||
{% assign text = "Changes" %}
|
||||
{% when "deprecations" %}
|
||||
{% assign text = "Deprecations" %}
|
||||
{% when "backups" %}
|
||||
{% assign text = "Backups" %}
|
||||
{% else %}
|
||||
{% assign text = "INVALID SECTION" %}
|
||||
{% endcase %}
|
||||
|
||||
<span class="px-3 py-2 text-small text-bold text-uppercase text-mono color-text-inverse release-notes-section-label">{{ text }}</span>
|
|
@ -1,9 +0,0 @@
|
|||
<div class="my-6 pt-6">
|
||||
<h2 class="mb-2 font-mktg h1" id="community-examples"><a href="#community-examples">{% data ui.product_landing.sponsor_community %}</a></h2>
|
||||
|
||||
<div class="d-flex flex-wrap gutter">
|
||||
{% render sponsors-community-card for productUserExamples as example %}
|
||||
|
||||
</div>
|
||||
<a href="https://github.com/sponsors/community" class="btn btn-outline float-right">{% data ui.product_landing.explore_people_and_projects %} {% octicon "arrow-right" %}</a>
|
||||
</div>
|
|
@ -1,14 +0,0 @@
|
|||
<div class="col-12 col-xl-4 col-lg-6 mb-4 js-filter-card {% if forloop.index0 > 5 %}d-none{% endif %}" data-repo="{{ example.user }}" data-description="{{ example.description }}">
|
||||
<a
|
||||
class="Box d-flex height-full color-shadow-medium hover-shadow-large no-underline color-text-primary p-4"
|
||||
href="https://github.com/sponsors/{{ example.user }}"
|
||||
>
|
||||
<div class="flex-shrink-0 mr-3">
|
||||
<img src="https://github.com/{{ example.user | split: '/' | first }}.png" alt="{{ example.user }}" class="avatar avatar-8 circle">
|
||||
</div>
|
||||
<div class="flex-auto">
|
||||
<h4>{{ example.user }}</h4>
|
||||
<p class="mt-1 color-text-tertiary">{{ example.description }}</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
|
@ -1,17 +0,0 @@
|
|||
export default function airgapLinks() {
|
||||
// @ts-ignore
|
||||
if (window.IS_NEXTJS_PAGE) return
|
||||
|
||||
// When in an airgapped environment,
|
||||
// show a tooltip on external links
|
||||
const exposeEl = document?.getElementById('expose') as HTMLScriptElement
|
||||
const { airgap } = JSON.parse(exposeEl.text)
|
||||
if (!airgap) return
|
||||
|
||||
const externaLinks = Array.from(document.querySelectorAll('a[href^="http"], a[href^="//"]'))
|
||||
externaLinks.forEach((link) => {
|
||||
link.classList.add('tooltipped')
|
||||
link.setAttribute('aria-label', 'This link may not work in this environment.')
|
||||
link.setAttribute('rel', 'noopener')
|
||||
})
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
/**
|
||||
* Handles the client-side events for `includes/all-articles.html`.
|
||||
*/
|
||||
export default function allArticles() {
|
||||
const buttons = document.querySelectorAll('button.js-all-articles-show-more')
|
||||
|
||||
buttons.forEach((btn) =>
|
||||
btn.addEventListener('click', (evt) => {
|
||||
const target = evt.currentTarget as HTMLButtonElement
|
||||
// Show all hidden links
|
||||
const hiddenLinks = target?.parentElement?.querySelectorAll('li.d-none')
|
||||
hiddenLinks?.forEach((link) => link.classList.remove('d-none'))
|
||||
// Remove the button, since we don't need it anymore
|
||||
target?.parentElement?.removeChild(target)
|
||||
})
|
||||
)
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
declare module 'browser-date-formatter' {
|
||||
export default function browserDateFormatter(): void
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
const expandText = 'Expand All'
|
||||
const closeText = 'Close All'
|
||||
|
||||
export default function devToc() {
|
||||
const expandButton = document.querySelector('.js-expand')
|
||||
if (!expandButton) return
|
||||
|
||||
const detailsElements = document.querySelectorAll('details')
|
||||
|
||||
expandButton.addEventListener('click', () => {
|
||||
// on click, toggle all the details elements open or closed
|
||||
const anyDetailsOpen = Array.from(detailsElements).find((details) => details.open)
|
||||
|
||||
detailsElements.forEach((detailsElement) => {
|
||||
anyDetailsOpen ? detailsElement.removeAttribute('open') : (detailsElement.open = true)
|
||||
})
|
||||
|
||||
// toggle the button text on click
|
||||
anyDetailsOpen
|
||||
? (expandButton.textContent = expandText)
|
||||
: (expandButton.textContent = closeText)
|
||||
})
|
||||
|
||||
// also toggle the button text on clicking any of the details elements
|
||||
detailsElements.forEach((detailsElement) => {
|
||||
detailsElement.addEventListener('click', () => {
|
||||
expandButton.textContent = closeText
|
||||
|
||||
// we can only get an accurate count of the open details elements if we wait a fraction after click
|
||||
setTimeout(() => {
|
||||
if (!Array.from(detailsElements).find((details) => details.open)) {
|
||||
expandButton.textContent = expandText
|
||||
}
|
||||
}, 50)
|
||||
})
|
||||
})
|
||||
}
|
|
@ -1,141 +0,0 @@
|
|||
function matchCardBySearch(card: HTMLElement, searchString: string) {
|
||||
const matchReg = new RegExp(searchString, 'i')
|
||||
// Check if this card matches - any `data-*` attribute contains the string
|
||||
return Object.keys(card.dataset).some((key) => matchReg.test(card.dataset[key] || ''))
|
||||
}
|
||||
|
||||
function matchCardByAttribute(card: HTMLElement, attribute: string, value: string): boolean {
|
||||
if (attribute in card.dataset) {
|
||||
const allValues = (card.dataset[attribute] || '').split(',')
|
||||
return allValues.some((key) => key === value)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export default function cardsFilter() {
|
||||
const inputFilter = document.querySelector('.js-filter-card-filter') as HTMLInputElement
|
||||
const dropdownFilters = Array.from(
|
||||
document.querySelectorAll('.js-filter-card-filter-dropdown')
|
||||
) as Array<HTMLSelectElement>
|
||||
const cards = Array.from(document.querySelectorAll('.js-filter-card')) as Array<HTMLElement>
|
||||
const showMoreButton = document.querySelector('.js-filter-card-show-more') as HTMLButtonElement
|
||||
const noResults = document.querySelector('.js-filter-card-no-results') as HTMLElement
|
||||
// if jsFilterCardMax not set, assume no limit (well, at 99)
|
||||
// some landing pages don't include the button because the number of
|
||||
// guides is less than the max defined in includes/article-cards.html
|
||||
const maxCards = parseInt(showMoreButton?.dataset?.jsFilterCardMax || '') || 99
|
||||
|
||||
const noFilter = () => {
|
||||
if (showMoreButton) showMoreButton.classList.remove('d-none')
|
||||
for (let index = 0; index < cards.length; index++) {
|
||||
const card = cards[index]
|
||||
// Hide all but the first n number of cards
|
||||
if (index > maxCards - 1) {
|
||||
card.classList.add('d-none')
|
||||
} else {
|
||||
card.classList.remove('d-none')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const filterEventHandler = (evt: Event) => {
|
||||
const currentTarget = evt.currentTarget as HTMLSelectElement | HTMLInputElement
|
||||
const value = currentTarget.value
|
||||
|
||||
if (showMoreButton) showMoreButton.classList.add('d-none')
|
||||
|
||||
// Track whether or not we had at least one match
|
||||
let hasMatches = false
|
||||
|
||||
for (let index = 0; index < cards.length; index++) {
|
||||
const card = cards[index] as HTMLElement
|
||||
|
||||
let cardMatches = false
|
||||
|
||||
if (currentTarget.tagName === 'INPUT') {
|
||||
// Filter was emptied
|
||||
if (!value) {
|
||||
noFilter()
|
||||
// return hasMatches = true, so we don't show the "No results" blurb
|
||||
hasMatches = true
|
||||
continue
|
||||
}
|
||||
cardMatches = matchCardBySearch(card, value)
|
||||
}
|
||||
|
||||
if (currentTarget.tagName === 'SELECT' && currentTarget.name) {
|
||||
const matches: Array<boolean> = []
|
||||
// check all the other dropdowns
|
||||
dropdownFilters.forEach(({ name, value }) => {
|
||||
if (!name || !value) return
|
||||
matches.push(matchCardByAttribute(card, name, value))
|
||||
})
|
||||
// if none of the filters is selected
|
||||
if (matches.length === 0) {
|
||||
noFilter()
|
||||
// return hasMatches = true, so we don't show the "No results" blurb
|
||||
hasMatches = true
|
||||
continue
|
||||
}
|
||||
cardMatches = matches.every((value) => value)
|
||||
}
|
||||
|
||||
if (cardMatches) {
|
||||
card.classList.remove('d-none')
|
||||
hasMatches = true
|
||||
} else {
|
||||
card.classList.add('d-none')
|
||||
}
|
||||
}
|
||||
|
||||
// If there wasn't at least one match, show the "no results" text
|
||||
if (!hasMatches) {
|
||||
noResults?.classList.remove('d-none')
|
||||
} else {
|
||||
noResults?.classList.add('d-none')
|
||||
}
|
||||
|
||||
return hasMatches
|
||||
}
|
||||
|
||||
if (inputFilter) {
|
||||
inputFilter.addEventListener('keyup', (evt) => {
|
||||
const hasMatches = filterEventHandler(evt)
|
||||
if (!hasMatches) {
|
||||
const cardValueEl = document.querySelector('.js-filter-card-value')
|
||||
if (cardValueEl) cardValueEl.textContent = (evt.currentTarget as HTMLInputElement)?.value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (dropdownFilters) {
|
||||
dropdownFilters.forEach((filter) => filter.addEventListener('change', filterEventHandler))
|
||||
}
|
||||
|
||||
if (showMoreButton) {
|
||||
showMoreButton.addEventListener('click', (evt: MouseEvent) => {
|
||||
// Number of cards that are currently visible
|
||||
const numShown = cards.filter((card) => !card.classList.contains('d-none')).length
|
||||
// We want to show n more cards
|
||||
const totalToShow = numShown + maxCards
|
||||
|
||||
for (let index = numShown; index < cards.length; index++) {
|
||||
const card = cards[index]
|
||||
|
||||
// If the card we're at is less than the total number of cards
|
||||
// we should show, show this one
|
||||
if (index < totalToShow) {
|
||||
card.classList.remove('d-none')
|
||||
} else {
|
||||
// Otherwise, we've shown the ones we intend to so exit the loop
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// They're all shown now, we should hide the button
|
||||
if (totalToShow >= cards.length) {
|
||||
;(evt?.currentTarget as HTMLElement)?.classList.add('d-none')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -5,47 +5,25 @@ import displayToolSpecificContent from './display-tool-specific-content'
|
|||
import explorer from './explorer'
|
||||
import scrollUp from './scroll-up'
|
||||
import search from './search'
|
||||
import nav from './nav'
|
||||
import browserDateFormatter from 'browser-date-formatter'
|
||||
import sidebar from './sidebar'
|
||||
import wrapCodeTerms from './wrap-code-terms'
|
||||
import print from './print'
|
||||
import localization from './localization'
|
||||
import survey from './survey'
|
||||
import experiment from './experiment'
|
||||
import copyCode from './copy-code'
|
||||
import initializeEvents from './events'
|
||||
import filterCards from './filter-cards'
|
||||
import allArticles from './all-articles'
|
||||
import devToc from './dev-toc'
|
||||
import releaseNotes from './release-notes'
|
||||
import showMore from './show-more'
|
||||
import airgapLinks from './airgap-links'
|
||||
import toggleImages from './toggle-images'
|
||||
import setNextEnv from './set-next-env'
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
setNextEnv()
|
||||
displayPlatformSpecificContent()
|
||||
displayToolSpecificContent()
|
||||
explorer()
|
||||
scrollUp()
|
||||
search()
|
||||
nav()
|
||||
browserDateFormatter()
|
||||
sidebar()
|
||||
wrapCodeTerms()
|
||||
print()
|
||||
localization()
|
||||
copyCode()
|
||||
filterCards()
|
||||
allArticles()
|
||||
devToc()
|
||||
showMore()
|
||||
airgapLinks()
|
||||
releaseNotes()
|
||||
initializeEvents()
|
||||
experiment()
|
||||
survey()
|
||||
toggleImages()
|
||||
})
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
export default function () {
|
||||
// Open and close mobile nav
|
||||
const hamburgerButton = document.querySelector('.nav-mobile-burgerIcon')
|
||||
const mobileDropdown = document.querySelector('.nav-mobile-dropdown')
|
||||
|
||||
if (!(hamburgerButton && mobileDropdown)) return
|
||||
|
||||
hamburgerButton.addEventListener('click', (event) => {
|
||||
event.preventDefault()
|
||||
hamburgerButton.classList.toggle('js-open')
|
||||
mobileDropdown.classList.toggle('js-open')
|
||||
})
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
export default function releaseNotes() {
|
||||
// @ts-ignore
|
||||
if (window.IS_NEXTJS_PAGE) return
|
||||
|
||||
const patches = Array.from(document.querySelectorAll('.js-release-notes-patch'))
|
||||
if (patches.length === 0) return
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
for (const entry of entries) {
|
||||
const { version } = (entry.target as HTMLElement).dataset
|
||||
const patchLink = document.querySelector(
|
||||
`.js-release-notes-patch-link[data-version="${version}"]`
|
||||
)
|
||||
patchLink?.classList.toggle('selected', entry.isIntersecting)
|
||||
}
|
||||
},
|
||||
{
|
||||
rootMargin: '-40% 0px -50%',
|
||||
}
|
||||
)
|
||||
|
||||
patches.forEach((patch) => {
|
||||
observer.observe(patch)
|
||||
})
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
export default function setNextEnv() {
|
||||
// @ts-ignore
|
||||
window.IS_NEXTJS_PAGE = !!document.querySelector('#__next')
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* This utility component implement a list of items, some of which are hidden initially
|
||||
* until user clicks "show more".
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* <div class="js-show-more-container">
|
||||
* <div class="js-show-more-item">item</div>
|
||||
* <div class="js-show-more-item d-none">hidden item</div>
|
||||
* <div class="js-show-more-item d-none">hidden item</div>
|
||||
* <button class="js-show-more-button" data-js-show-more-items="1">show one more item</button>
|
||||
* </div>
|
||||
*/
|
||||
|
||||
export default function showMore() {
|
||||
const buttons = document.querySelectorAll('.js-show-more-button')
|
||||
|
||||
buttons.forEach((btn) => {
|
||||
btn.addEventListener('click', (evt) => {
|
||||
const target = evt.currentTarget as HTMLButtonElement
|
||||
const container = target.closest('.js-show-more-container')
|
||||
if (!container) return
|
||||
const hiddenLinks = container.querySelectorAll('.js-show-more-item.d-none')
|
||||
// get number of items to show more of, if not set, show all remaining items
|
||||
const showMoreNum = target.dataset.jsShowMoreItems || hiddenLinks.length
|
||||
let count = 0
|
||||
for (const link of Array.from(hiddenLinks)) {
|
||||
if (count++ >= showMoreNum) {
|
||||
break
|
||||
}
|
||||
link.classList.remove('d-none')
|
||||
}
|
||||
// Remove the button if all items have been shown
|
||||
if (container.querySelectorAll('.js-show-more-item.d-none').length === 0) {
|
||||
target?.parentElement?.removeChild(target)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
export default function () {
|
||||
// TODO override active classes set on server side if sidebar elements are clicked
|
||||
|
||||
const activeMenuItem = document.querySelector('.sidebar .active') as HTMLElement
|
||||
if (!activeMenuItem) return
|
||||
|
||||
const verticalBufferAboveActiveItem = 40
|
||||
const activeMenuItemPosition = activeMenuItem.offsetTop - verticalBufferAboveActiveItem
|
||||
const menu = document.querySelector('.sidebar')
|
||||
|
||||
if (activeMenuItemPosition > window.innerHeight * 0.5) {
|
||||
menu?.scrollTo(0, activeMenuItemPosition)
|
||||
}
|
||||
|
||||
// if the active category is a standalone category, do not close the other open dropdowns
|
||||
const activeStandaloneCategory = document.querySelectorAll(
|
||||
'.sidebar-category.active.standalone-category'
|
||||
)
|
||||
if (activeStandaloneCategory.length) return
|
||||
|
||||
const allOpenDetails = document.querySelectorAll('.sidebar-category:not(.active) details[open]')
|
||||
|
||||
if (allOpenDetails) {
|
||||
for (const openDetail of Array.from(allOpenDetails)) {
|
||||
openDetail.removeAttribute('open')
|
||||
const svgArrowElem = openDetail.querySelector('summary > div > svg')
|
||||
svgArrowElem?.remove()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
import { sendEvent, EventType } from './events'
|
||||
|
||||
function showElement(el: HTMLElement) {
|
||||
el.removeAttribute('hidden')
|
||||
}
|
||||
|
||||
function hideElement(el: HTMLElement) {
|
||||
el.setAttribute('hidden', 'hidden')
|
||||
}
|
||||
|
||||
function updateDisplay(form: HTMLFormElement, state: string) {
|
||||
const allSelector = ['start', 'yes', 'no', 'end']
|
||||
.map((xstate) => '[data-help-' + xstate + ']')
|
||||
.join(',')
|
||||
const stateSelector = '[data-help-' + state + ']'
|
||||
const allEls = Array.from(form.querySelectorAll(allSelector)) as Array<HTMLElement>
|
||||
allEls.forEach(hideElement)
|
||||
const stateEls = Array.from(form.querySelectorAll(stateSelector)) as Array<HTMLElement>
|
||||
stateEls.forEach(showElement)
|
||||
}
|
||||
|
||||
function submitForm(form: HTMLFormElement) {
|
||||
const formData = new FormData(form)
|
||||
return trackEvent(formData)
|
||||
}
|
||||
|
||||
function trackEvent(formData: FormData) {
|
||||
return sendEvent({
|
||||
type: EventType.survey,
|
||||
survey_token: (formData.get('survey-token') as string) || undefined, // Honeypot
|
||||
survey_vote: formData.get('survey-vote') === 'Yes',
|
||||
survey_comment: (formData.get('survey-comment') as string) || undefined,
|
||||
survey_email: (formData.get('survey-email') as string) || undefined,
|
||||
})
|
||||
}
|
||||
|
||||
export default function survey() {
|
||||
// @ts-ignore
|
||||
if (window.IS_NEXTJS_PAGE) return
|
||||
|
||||
const form = document.querySelector('.js-survey') as HTMLFormElement | null
|
||||
const texts = Array.from(
|
||||
document.querySelectorAll('.js-survey input, .js-survey textarea')
|
||||
) as Array<HTMLElement>
|
||||
const votes = Array.from(document.querySelectorAll('.js-survey [type=radio]'))
|
||||
if (!form || !texts.length || !votes.length) return
|
||||
|
||||
form.addEventListener('submit', (evt) => {
|
||||
evt.preventDefault()
|
||||
submitForm(form)
|
||||
updateDisplay(form, 'end')
|
||||
})
|
||||
|
||||
votes.forEach((voteEl) => {
|
||||
voteEl.addEventListener('change', (evt) => {
|
||||
const radio = evt.target as HTMLInputElement
|
||||
const state = radio.value.toLowerCase()
|
||||
submitForm(form)
|
||||
updateDisplay(form, state)
|
||||
})
|
||||
})
|
||||
|
||||
// Prevent the site search from overtaking your input
|
||||
texts.forEach((text) => {
|
||||
text.addEventListener('keydown', (evt: KeyboardEvent) => {
|
||||
if (evt.code === 'Slash') evt.stopPropagation()
|
||||
})
|
||||
})
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
<!doctype html>
|
||||
<html lang="{{currentLanguage}}" data-color-mode="$COLORMODE$" data-dark-theme="$DARKTHEME$" data-light-theme="$LIGHTTHEME$">
|
||||
{% include head %}
|
||||
|
||||
<body class="d-lg-flex">
|
||||
{% include sidebar %}
|
||||
|
||||
<main class="width-full">
|
||||
{% include header %}
|
||||
{% include deprecation-banner %}
|
||||
{% if page.relativePath == 'index.md' %}
|
||||
{% include landing %}
|
||||
{% else %}
|
||||
{% include article %}
|
||||
{% endif %}
|
||||
{% include support-section %}
|
||||
{% include small-footer %}
|
||||
{% include scroll-button %}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
|
@ -1,68 +0,0 @@
|
|||
<!doctype html>
|
||||
<html lang="{{currentLanguage}}" data-color-mode="$COLORMODE$" data-dark-theme="$DARKTHEME$" data-light-theme="$LIGHTTHEME$">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Docs TOC</title>
|
||||
<link rel="stylesheet" href="{{ builtAssets.main.css }}">
|
||||
<link rel="alternate icon" type="image/png" href="/assets/images/site/favicon.png">
|
||||
<link rel="icon" type="image/svg+xml" href="/assets/images/site/favicon.svg">
|
||||
</head>
|
||||
|
||||
<body class="dev-toc p-3 m-3">
|
||||
<main class="width-full">
|
||||
|
||||
<h3>Versions</h3>
|
||||
<ul class="versions-list">
|
||||
{% for version in allVersions %}
|
||||
{% if version[0] == "free-pro-team@latest" %}
|
||||
<li><a href="/dev-toc">{{ version[1].versionTitle }}</a>
|
||||
{% else %}
|
||||
<li><a href="/{{ version[0] }}/dev-toc">{{ version[1].versionTitle }}</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
{% if allVersions[devTocVersion] %}
|
||||
<h2 class="mt-3 mb-3"><abbr>TOC</abbr> for {{ allVersions[devTocVersion].versionTitle }}</h2>
|
||||
|
||||
<button class="btn mb-3 js-expand" type="button">Expand All</button>
|
||||
<div/>
|
||||
|
||||
{% for productPage in devTocTree.childPages %}
|
||||
<details class="mb-1"><summary>{{productPage.renderedFullTitle}}</summary>
|
||||
<ul class="products-list">
|
||||
<li title="{{productPage.renderedFullTitle}}">
|
||||
<a title="{{ productPage.page.documentType }}" href="{{productPage.href}}">{{ productPage.renderedFullTitle }}</a>
|
||||
{% for categoryPage in productPage.childPages %}
|
||||
<ul>
|
||||
<li>
|
||||
<a title="{{ categoryPage.page.documentType }}" href="{{ categoryPage.href }}">{{ categoryPage.renderedFullTitle }}</a>
|
||||
<ul>
|
||||
<!-- The following may actually be child articles of categories in some cases,
|
||||
e.g., github/site-policy, not map topics -->
|
||||
{% for maptopicPage in categoryPage.childPages %}
|
||||
<li>
|
||||
<a title="{{ maptopicPage.page.documentType }}" href="{{ maptopicPage.href }}">{{ maptopicPage.renderedFullTitle }}</a>
|
||||
<ul>
|
||||
{% for articlePage in maptopicPage.childPages %}
|
||||
<li>
|
||||
<a title="{{ articlePage.page.documentType }}" href="{{ articlePage.href }}">{{ articlePage.renderedFullTitle }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</details>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% include scripts %}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
|
@ -1,41 +0,0 @@
|
|||
<!doctype html>
|
||||
<html lang="{{currentLanguage}}" data-color-mode="$COLORMODE$" data-dark-theme="$DARKTHEME$" data-light-theme="$LIGHTTHEME$">
|
||||
{% include head %}
|
||||
|
||||
<body class="d-lg-flex">
|
||||
{% include sidebar %}
|
||||
|
||||
<main class="width-full">
|
||||
{% include header %}
|
||||
{% include deprecation-banner %}
|
||||
|
||||
<main class="container-xl px-3 px-md-6 my-4 my-lg-4 d-xl-flex">
|
||||
<article class="markdown-body width-full">
|
||||
<div class="d-lg-flex flex-justify-between">
|
||||
<div class="d-flex flex-items-center breadcrumbs-wrapper">
|
||||
{% include breadcrumbs %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 class="border-bottom-0">{{ page.title }}</h1>
|
||||
|
||||
<div class="mt-2">
|
||||
<div>
|
||||
{% if AIRGAP %}
|
||||
<p>GraphQL explorer is not available on this environment.</p>
|
||||
{% else %}
|
||||
<iframe id="graphiql" class="graphql-explorer" scrolling="no" src="{{ graphql.explorerUrl }}">
|
||||
<p>You must have iframes enabled to use this feature.</p>
|
||||
</iframe>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</main>
|
||||
|
||||
{% include support-section %}
|
||||
{% include small-footer %}
|
||||
{% include scroll-button %}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
|
@ -1,168 +0,0 @@
|
|||
<!doctype html>
|
||||
<html lang="{{currentLanguage}}" data-color-mode="$COLORMODE$" data-dark-theme="$DARKTHEME$" data-light-theme="$LIGHTTHEME$">
|
||||
{% include head %}
|
||||
|
||||
<body class="d-lg-flex">
|
||||
{% include sidebar %}
|
||||
|
||||
<main class="width-full">
|
||||
{% include header %}
|
||||
{% include deprecation-banner %}
|
||||
|
||||
<div class="container-xl px-3 px-md-6 pt-3 pb-2 product-landing">
|
||||
<header class="d-lg-flex gutter-lg mb-6">
|
||||
<div class="{% if page.product_video and page.product_video != '' %}col-12 col-lg-6 mb-3 mb-lg-0{% endif %}">
|
||||
<span class="text-mono color-text-secondary">Product</span>
|
||||
<h1 class="mb-3 font-mktg">
|
||||
{{ page.shortTitle }}
|
||||
|
||||
{% if page.beta_product %}
|
||||
<span class="Label Label--success v-align-middle">Beta</span>
|
||||
{% endif %}
|
||||
</h1>
|
||||
<div class="lead-mktg color-text-secondary">{{ page.intro }}</div>
|
||||
|
||||
{% for introLinkType in page.introLinks %}
|
||||
{% if introLinkType[1] != '' %}
|
||||
{% unless introLinkType[0] contains 'raw' %}
|
||||
<a href="/{{ currentLanguage }}{% if currentVersion != 'free-pro-team@latest' %}/{{ currentVersion }}{% endif %}{{ page.introLinks[introLinkType[0]] }}" class="btn-mktg {% unless forloop.first %}btn-outline-mktg {% endunless %}btn-large f4 mt-3 mr-3">
|
||||
{% data ui.product_landing[introLinkType[0]] %}
|
||||
</a>
|
||||
{% endunless %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% if page.product_video and page.product_video != '' %}
|
||||
<div class="col-12 col-lg-6">
|
||||
<div class="position-relative" style="padding-bottom:56.25%;">
|
||||
{% unless AIRGAP %}
|
||||
<iframe
|
||||
title="{{ page.shortTitle }} Video"
|
||||
class="top-0 left-0 position-absolute color-shadow-large rounded-1 width-full height-full"
|
||||
src="{{ page.product_video }}"
|
||||
frameborder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen
|
||||
></iframe>
|
||||
{% endunless %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</header>
|
||||
|
||||
<!-- Article links -->
|
||||
<div class="d-lg-flex gutter my-6 py-6">
|
||||
<div class="col-12 col-lg-{% if whatsNewChangelog %}4{% else %}6{% endif %} mb-4 mb-lg-0">
|
||||
<div class="featured-links-heading mb-4 d-flex flex-items-baseline">
|
||||
<h3 class="f4 text-normal text-mono text-uppercase color-text-secondary" id="guides"><a href="#guides">{% data ui.toc.guides %}</a></h3>
|
||||
{% unless currentCategory %}
|
||||
{% if page.children contains "/guides" %}
|
||||
<a href="{{ currentPath }}/guides" class="ml-4">View all {% octicon "arrow-right" height="14" class="v-align-middle" %}</a>
|
||||
{% endif %}
|
||||
{% endunless %}
|
||||
</div>
|
||||
<ul class="list-style-none">
|
||||
{% for link in featuredLinks.guides %}
|
||||
<li class="border-top">
|
||||
<a class="link-with-intro Bump-link--hover no-underline d-block py-3" href="{{ link.href }}">
|
||||
<h4 class="link-with-intro-title mb-1">{{ link.title }}<span class="Bump-link-symbol">→</span></h4>
|
||||
<p class="link-with-intro-intro color-text-secondary mb-0">{{ link.intro | truncatewords: 12 }}</p>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{% if featuredLinks.popular %}
|
||||
<div class="col-12 col-lg-{% if whatsNewChangelog %}4{% else %}6{% endif %} mb-4 mb-lg-0">
|
||||
<div class="featured-links-heading mb-4 d-flex flex-items-baseline">
|
||||
<h3 class="f4 text-normal text-mono text-uppercase color-text-secondary" id="popular"><a href="#popular">{% if page.featuredLinks.popularHeading %}{{ page.featuredLinks.popularHeading }}{% else %}{% data ui.toc.popular %}{% endif %}</a></h3>
|
||||
</div>
|
||||
<ul class="list-style-none">
|
||||
{% for link in featuredLinks.popular %}
|
||||
<li class="border-top">
|
||||
<a class="link-with-intro Bump-link--hover no-underline d-block py-3" href="{{ link.href }}">
|
||||
<h2 class="link-with-intro-title f5">{{ link.title }}<span class="Bump-link-symbol">→</span></h2>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if whatsNewChangelog %}
|
||||
<div class="col-12 col-lg-4 mb-4 mb-lg-0">
|
||||
<div class="featured-links-heading mb-4 d-flex flex-items-baseline">
|
||||
<h3 class="f4 text-normal text-mono text-uppercase color-text-secondary" id="whats-new"><a href="#whats-new">{% data ui.toc.whats_new %}</a></h3>
|
||||
<a href="{{ changelogUrl }}" class="ml-4">View all {% octicon "arrow-right" height="14" class="v-align-middle" %}</a>
|
||||
</div>
|
||||
<ul class="list-style-none">
|
||||
{% for link in whatsNewChangelog %}
|
||||
<li class="border-top">
|
||||
<a href="{{ link.href }}" class="d-block color-text-primary Bump-link--hover py-3 no-underline capitalize">
|
||||
<h4>{{ link.title }} <span class="Bump-link-symbol">→</span></h4>
|
||||
<time
|
||||
class="tooltipped tooltipped-n color-text-tertiary text-mono mt-1"
|
||||
aria-label="{{ link.date | date: '%B %d, %Y' }}"
|
||||
>{{ link.date | date: "%B %d" }}</time>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if productCodeExamples %}
|
||||
{% include code-examples %}
|
||||
{% endif %}
|
||||
|
||||
{% if productCommunityExamples %}
|
||||
{% include community-examples %}
|
||||
{% endif %}
|
||||
|
||||
{% if productUserExamples %}
|
||||
{% include sponsor-examples %}
|
||||
{% endif %}
|
||||
|
||||
{{ renderedPage }}
|
||||
</div>
|
||||
|
||||
{% if currentVersion contains 'enterprise-server' and currentProduct == 'admin' %}
|
||||
{% include product-releases %}
|
||||
{% endif %}
|
||||
|
||||
{% if featuredLinks.guideCards %}
|
||||
<div class="color-bg-tertiary py-6">
|
||||
<div class="container-xl px-3 px-md-6 my-6">
|
||||
<h2 class="font-mktg h1 mb-2" id="guides-2"><a href="#guides-2">Guides</a></h2>
|
||||
|
||||
<div class="d-lg-flex gutter-lg flex-items-stretch">
|
||||
{% assign guideCards = featuredLinks.guideCards %}
|
||||
{% render guide-card for guideCards as guide %}
|
||||
</div>
|
||||
|
||||
{% unless currentCategory %}
|
||||
{% if page.children contains "/guides" %}
|
||||
<a href="{{ currentPath }}/guides" class="btn btn-outline float-right">Explore guides {% octicon "arrow-right" %}</a>
|
||||
{% endif %}
|
||||
{% endunless %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="container-xl px-3 px-md-6 mt-6">
|
||||
{% if page.documentType == "category" %}
|
||||
{% include category-articles-list %}
|
||||
{% endif %}
|
||||
{% if page.documentType == "product" %}
|
||||
{% include product-articles-list %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% include support-section %}
|
||||
{% include small-footer %}
|
||||
{% include scroll-button %}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
|
@ -1,122 +0,0 @@
|
|||
<!doctype html>
|
||||
{% assign guideTypes = site.data.ui.product_sublanding.guide_types %}
|
||||
|
||||
<html lang="{{currentLanguage}}" data-color-mode="$COLORMODE$" data-dark-theme="$DARKTHEME$" data-light-theme="$LIGHTTHEME$">
|
||||
{% include head %}
|
||||
|
||||
<body class="d-lg-flex">
|
||||
{% include sidebar %}
|
||||
|
||||
<main class="width-full overflow-auto">
|
||||
{% include header %}
|
||||
{% include deprecation-banner %}
|
||||
|
||||
<div class="color-bg-tertiary">
|
||||
<div class="container-xl px-3 px-md-6 pt-3 pb-2">
|
||||
<header class="d-flex gutter mb-6">
|
||||
<div class="col-12">
|
||||
<span class="text-mono color-text-secondary text-uppercase">{% include breadcrumbs %}</span>
|
||||
<h1 class="my-3 font-mktg">{% data ui.product_sublanding.guides %}</h1>
|
||||
<div class="lead-mktg color-text-secondary f4 description-text">{{ page.intro }}</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Featured track -->
|
||||
{% if page.featuredTrack %}
|
||||
{% assign featuredTrack = page.featuredTrack %}
|
||||
<div class="mb-6 position-relative overflow-hidden mr-n3 ml-n3 px-3">
|
||||
<ul class="list-style-none d-flex flex-nowrap overflow-x-scroll px-2 feature-track">
|
||||
<li class="px-2 d-flex flex-shrink-0">
|
||||
<div class="d-inline-block Box p-5 bg-gradient--blue-pink color-text-inverse">
|
||||
<div class="circle color-text-inverse d-inline-flex" style="border: 2px white solid;">{% octicon "star-fill" height="24" class="v-align-middle m-2"%}</div>
|
||||
<h3 class="font-mktg h3-mktg my-4">{{ featuredTrack.title }}</h3>
|
||||
<div class="lead-mktg color-text-inverse f5 my-4">{{ featuredTrack.description }}</div>
|
||||
<a class="d-inline-block border color-border-inverse color-text-inverse px-4 py-2 f5 no-underline text-bold" role="button" href="{{ featuredTrack.guides[0].href }}?learn={{ featuredTrack.trackName }}">
|
||||
<span class="mr-2">{% octicon "arrow-right" height="20" %}</span>
|
||||
{% data ui.product_sublanding.start_path %}
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
{% for guide in featuredTrack.guides %}
|
||||
<li class="px-2 d-flex flex-shrink-0">
|
||||
<a href="{{ guide.href }}?learn={{ featuredTrack.trackName }}" class="d-inline-block Box p-5 color-bg-primary color-border-primary no-underline">
|
||||
<div class="d-flex flex-justify-between flex-items-center">
|
||||
<div class="circle color-bg-primary color-text-link border-gradient--pink-blue-dark d-inline-flex">
|
||||
<span class="m-2 f2 lh-condensed-ultra text-center text-bold step-circle-text" style="width: 24px; height: 24px;">{{ forloop.index }}</span>
|
||||
</div>
|
||||
<div class="color-text-tertiary h6 text-uppercase">{{ guideTypes[guide.page.type] }}</div>
|
||||
</div>
|
||||
<h3 class="font-mktg h3-mktg my-4 color-text-primary">{{ guide.title }}</h3>
|
||||
<div class="lead-mktg color-text-secondary f5 my-4 truncate-overflow-8">{{ guide.intro }}</div>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<div class="position-absolute top-0 bottom-0 left-0 ml-3 pl-3 fade-background-left"></div>
|
||||
<div class="position-absolute top-0 bottom-0 right-0 mr-3 pr-3 fade-background-right"></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% assign learningTracks = page.learningTracks %}
|
||||
{% if learningTracks and learningTracks.size > 0 %}
|
||||
<div class="border-top py-6">
|
||||
<h2 class="mb-3 font-mktg" id="learning-paths"><a href="#learning-paths">{% data ui.product_sublanding.learning_paths %}</a></h2>
|
||||
<div class="lead-mktg color-text-secondary f4 description-text">{% data ui.product_sublanding.learning_paths_desc %}</div>
|
||||
|
||||
<!-- Learning tracks -->
|
||||
<div class="d-flex flex-wrap flex-items-start my-5 gutter">
|
||||
{% for track in learningTracks %}
|
||||
<div class="my-3 px-4 col-12 col-md-6 learning-track">
|
||||
<div class="Box js-show-more-container d-flex flex-column">
|
||||
<div class="Box-header bg-gradient--blue-pink p-4 d-flex flex-1 flex-items-start flex-wrap">
|
||||
<div class="d-flex flex-auto flex-items-start col-8 col-md-12 col-xl-8">
|
||||
<div class="my-xl-0 mr-xl-3">
|
||||
<h5 class="mb-3 color-text-inverse font-mktg h3-mktg ">
|
||||
{{ track.title }}
|
||||
</h5>
|
||||
<p class="color-text-inverse truncate-overflow-3 learning-track--description">{{ track.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<a class="d-inline-block border color-border-inverse color-text-inverse px-3 py-2 f5 no-underline text-bold no-wrap mt-3 mt-md-0" role="button" href="{{ track.guides[0].href }}?learn={{ track.trackName }}">
|
||||
{% data ui.product_sublanding.start %}
|
||||
<span class="ml-2">{% octicon "arrow-right" height="20" %}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
{% for guide in track.guides %}
|
||||
<a class="Box-row d-flex flex-items-center color-text-primary no-underline js-show-more-item {% if forloop.index > 4 %}d-none{% endif %}" href="{{ guide.href }}?learn={{ track.trackName }}">
|
||||
<div class="circle color-bg-tertiary d-inline-flex mr-4">
|
||||
<span class="m-2 f3 lh-condensed-ultra text-center text-bold step-circle-text" style="min-width: 20px; height: 20px;">{{ forloop.index }}</span>
|
||||
</div>
|
||||
<h5 class="flex-auto pr-2">{{ guide.title }}</h5>
|
||||
<div class="color-text-tertiary h6 text-uppercase">{{ guideTypes[guide.page.type] }}</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if track.guides.size > 4 %}
|
||||
<a class="Box-footer btn-link border-top-0 position-relative text-center text-bold color-text-link pt-1 pb-3 col-12 js-show-more-button">
|
||||
<div class="position-absolute left-0 right-0 py-5 fade-background-bottom" style="; bottom: 50px;"></div>
|
||||
<span>{{ track.guides.size | minus: 4 }} {% data ui.product_sublanding.more_guides %}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if page.includeGuides %}
|
||||
<div class="py-6 border-top color-border-primary">
|
||||
{% include article-cards %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% include support-section %}
|
||||
{% include small-footer %}
|
||||
{% include scroll-button %}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
|
@ -1,25 +0,0 @@
|
|||
<!doctype html>
|
||||
<html lang="{{currentLanguage}}" data-color-mode="$COLORMODE$" data-dark-theme="$DARKTHEME$" data-light-theme="$LIGHTTHEME$">
|
||||
{% include head %}
|
||||
|
||||
<body class="d-lg-flex">
|
||||
{% include sidebar %}
|
||||
|
||||
<main class="flex-1 min-width-0">
|
||||
{% include header %}
|
||||
{% include deprecation-banner %}
|
||||
|
||||
{% if allVersions[currentVersion].plan == 'enterprise-server' %}
|
||||
{% include enterprise-server-release-notes %}
|
||||
{% endif %}
|
||||
|
||||
{% if allVersions[currentVersion].plan == 'github-ae' %}
|
||||
{% include github-ae-release-notes %}
|
||||
{% endif %}
|
||||
|
||||
{% include support-section %}
|
||||
{% include small-footer %}
|
||||
{% include scroll-button %}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
|
@ -2,11 +2,10 @@ import fs from 'fs'
|
|||
import path from 'path'
|
||||
import parse from './read-frontmatter.js'
|
||||
import semver from 'semver'
|
||||
import layouts from './layouts.js'
|
||||
import xAllVersions from './all-versions.js'
|
||||
|
||||
const layoutNames = ['default', 'dev-toc', 'graphql-explorer', 'product-landing', 'product-sublanding', 'release-notes', false]
|
||||
const semverValidRange = semver.validRange
|
||||
const layoutNames = Object.keys(layouts).concat([false])
|
||||
const semverRange = {
|
||||
type: 'string',
|
||||
conform: semverValidRange,
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
import { fileURLToPath } from 'url'
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import xWalkSync from 'walk-sync'
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
const walk = xWalkSync.entries
|
||||
const validLayoutExtensions = ['.md', '.html']
|
||||
const layoutsDirectory = path.join(__dirname, '../layouts')
|
||||
const layouts = {}
|
||||
|
||||
walk(layoutsDirectory, { directories: false })
|
||||
.filter((entry) => validLayoutExtensions.includes(path.extname(entry.relativePath)))
|
||||
.filter((entry) => !entry.relativePath.includes('README'))
|
||||
.forEach((entry) => {
|
||||
const key = path.basename(entry.relativePath).split('.').slice(0, -1).join('.')
|
||||
const fullPath = path.join(entry.basePath, entry.relativePath)
|
||||
const content = fs.readFileSync(fullPath, 'utf8')
|
||||
layouts[key] = content
|
||||
})
|
||||
|
||||
export default layouts
|
|
@ -82,7 +82,7 @@ async function getTocItems(pagesArray, context, isRecursive, renderIntros) {
|
|||
: null,
|
||||
childTocItems:
|
||||
isRecursive && child.childPages
|
||||
? getTocItems(child.childPages, context, isRecursive, renderIntros)
|
||||
? await getTocItems(child.childPages, context, isRecursive, renderIntros)
|
||||
: null,
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import layouts from '../../lib/layouts.js'
|
||||
|
||||
export default function layoutContext(req, res, next) {
|
||||
if (!req.context.page) return next()
|
||||
|
||||
|
@ -17,7 +15,6 @@ export default function layoutContext(req, res, next) {
|
|||
|
||||
// Attach to the context object
|
||||
req.context.currentLayoutName = layoutName
|
||||
req.context.currentLayout = layouts[layoutName]
|
||||
|
||||
return next()
|
||||
}
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
import { liquid } from '../lib/render-content/index.js'
|
||||
import layouts from '../lib/layouts.js'
|
||||
import nonEnterpriseDefaultVersion from '../lib/non-enterprise-default-version.js'
|
||||
|
||||
export default async function devToc(req, res, next) {
|
||||
if (process.env.NODE_ENV !== 'development') return next()
|
||||
if (!req.path.endsWith('/dev-toc')) return next()
|
||||
|
||||
req.context.devTocVersion =
|
||||
req.path === '/dev-toc' ? nonEnterpriseDefaultVersion : req.context.currentVersion
|
||||
|
||||
req.context.devTocTree = req.context.siteTree.en[req.context.devTocVersion]
|
||||
|
||||
const body = await liquid.parseAndRender(layouts['dev-toc'], req.context)
|
||||
|
||||
return res.status('200').send(body)
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { liquid } from '../lib/render-content/index.js'
|
||||
import layouts from '../lib/layouts.js'
|
||||
import FailBot from '../lib/failbot.js'
|
||||
import loadSiteData from '../lib/site-data.js'
|
||||
import builtAssets from '../lib/built-asset-urls.js'
|
||||
|
@ -53,7 +54,8 @@ export default async function handleError(error, req, res, next) {
|
|||
|
||||
// Special handling for when a middleware calls `next(404)`
|
||||
if (error === 404) {
|
||||
return res.status(404).send(await liquid.parseAndRender(layouts['error-404'], req.context))
|
||||
// Again, we can remove this once the 404/500 pages are ready
|
||||
return res.status(404).send(await liquid.parseAndRender(fs.readFileSync(path.join(process.cwd(), './layouts/error-404.html'), 'utf8'), req.context))
|
||||
}
|
||||
|
||||
// If the error contains a status code, just send that back. This is usually
|
||||
|
@ -66,8 +68,8 @@ export default async function handleError(error, req, res, next) {
|
|||
console.error('500 error!', req.path)
|
||||
console.error(error)
|
||||
}
|
||||
|
||||
res.status(500).send(await liquid.parseAndRender(layouts['error-500'], req.context))
|
||||
// Again, we can remove this once the 404/500 pages are ready
|
||||
res.status(500).send(await liquid.parseAndRender(fs.readFileSync(path.join(process.cwd(), './layouts/error-500.html'), 'utf8'), req.context))
|
||||
|
||||
// Report to Failbot AFTER responding to the user
|
||||
await logException(error, req)
|
||||
|
|
|
@ -53,10 +53,8 @@ import breadcrumbs from './contextualizers/breadcrumbs.js'
|
|||
import earlyAccessBreadcrumbs from './contextualizers/early-access-breadcrumbs.js'
|
||||
import features from './contextualizers/features.js'
|
||||
import productExamples from './contextualizers/product-examples.js'
|
||||
import devToc from './dev-toc.js'
|
||||
import featuredLinks from './featured-links.js'
|
||||
import learningTrack from './learning-track.js'
|
||||
import isNextRequest from './is-next-request.js'
|
||||
import next from './next.js'
|
||||
import renderPage from './render-page.js'
|
||||
|
||||
|
@ -212,18 +210,14 @@ export default function (app) {
|
|||
app.use(asyncMiddleware(instrument(features, './contextualizers/features')))
|
||||
app.use(asyncMiddleware(instrument(productExamples, './contextualizers/product-examples')))
|
||||
|
||||
app.use(asyncMiddleware(instrument(devToc, './dev-toc')))
|
||||
app.use(asyncMiddleware(instrument(featuredLinks, './featured-links')))
|
||||
app.use(asyncMiddleware(instrument(learningTrack, './learning-track')))
|
||||
app.use(asyncMiddleware(instrument(isNextRequest, './is-next-request')))
|
||||
|
||||
// *** Headers for pages only ***
|
||||
app.use(setFastlyCacheHeaders)
|
||||
|
||||
// handle serving NextJS bundled code (/_next/*)
|
||||
if (process.env.FEATURE_NEXTJS) {
|
||||
app.use(instrument(next, './next'))
|
||||
}
|
||||
app.use(instrument(next, './next'))
|
||||
|
||||
// Check for a dropped connection before proceeding (again)
|
||||
app.use(haltOnDroppedConnection)
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import next from 'next'
|
||||
|
||||
const { NODE_ENV, FEATURE_NEXTJS } = process.env
|
||||
const { NODE_ENV } = process.env
|
||||
const isDevelopment = NODE_ENV === 'development'
|
||||
|
||||
const nextApp = FEATURE_NEXTJS ? next({ dev: isDevelopment }) : null
|
||||
export const nextHandleRequest = nextApp ? nextApp.getRequestHandler() : null
|
||||
if (nextApp) {
|
||||
nextApp.prepare()
|
||||
}
|
||||
const nextApp = next({ dev: isDevelopment })
|
||||
export const nextHandleRequest = nextApp.getRequestHandler()
|
||||
nextApp.prepare()
|
||||
|
||||
function renderPageWithNext(req, res, next) {
|
||||
if (req.path.startsWith('/_next') && !req.path.startsWith('/_next/data')) {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { get } from 'lodash-es'
|
||||
import { liquid } from '../lib/render-content/index.js'
|
||||
import patterns from '../lib/patterns.js'
|
||||
import layouts from '../lib/layouts.js'
|
||||
import getMiniTocItems from '../lib/get-mini-toc-items.js'
|
||||
import Page from '../lib/page.js'
|
||||
import statsd from '../lib/statsd.js'
|
||||
|
@ -12,7 +13,6 @@ import { nextHandleRequest } from './next.js'
|
|||
const { HEROKU_RELEASE_VERSION } = process.env
|
||||
|
||||
const pageCacheDatabaseNumber = 1
|
||||
const pageCacheExpiration = 24 * 60 * 60 * 1000 // 24 hours
|
||||
|
||||
const pageCache = new RedisAccessor({
|
||||
databaseNumber: pageCacheDatabaseNumber,
|
||||
|
@ -57,7 +57,6 @@ function addColorMode(req, text) {
|
|||
|
||||
export default async function renderPage(req, res, next) {
|
||||
const page = req.context.page
|
||||
|
||||
// render a 404 page
|
||||
if (!page) {
|
||||
if (process.env.NODE_ENV !== 'test' && req.context.redirectNotFound) {
|
||||
|
@ -67,7 +66,8 @@ export default async function renderPage(req, res, next) {
|
|||
}
|
||||
return res
|
||||
.status(404)
|
||||
.send(modifyOutput(req, await liquid.parseAndRender(layouts['error-404'], req.context)))
|
||||
// We can get rid of reading the layout for 404 once we have the 404 page up and running
|
||||
.send(modifyOutput(req, await liquid.parseAndRender(fs.readFileSync(path.join(process.cwd(), './layouts/error-404.html'), 'utf8'), req.context)))
|
||||
}
|
||||
|
||||
if (req.method === 'HEAD') {
|
||||
|
@ -103,8 +103,6 @@ export default async function renderPage(req, res, next) {
|
|||
req.method === 'GET' &&
|
||||
// Skip for JSON debugging info requests
|
||||
!isRequestingJsonForDebugging &&
|
||||
// Skip for NextJS rendering
|
||||
!req.renderWithNextjs &&
|
||||
// Skip for airgapped sessions
|
||||
!isAirgapped &&
|
||||
// Skip for the GraphQL Explorer page
|
||||
|
@ -188,22 +186,8 @@ export default async function renderPage(req, res, next) {
|
|||
}
|
||||
}
|
||||
|
||||
// Hand rendering over to NextJS when appropriate
|
||||
if (req.renderWithNextjs) {
|
||||
req.context.renderedPage = context.renderedPage
|
||||
req.context.miniTocItems = context.miniTocItems
|
||||
return nextHandleRequest(req, res)
|
||||
}
|
||||
|
||||
// currentLayout is added to the context object in middleware/contextualizers/layouts
|
||||
const output = await liquid.parseAndRender(req.context.currentLayout, context)
|
||||
|
||||
// First, send the response so the user isn't waiting
|
||||
// NOTE: Do NOT `return` here as we still need to cache the response afterward!
|
||||
res.send(modifyOutput(req, output))
|
||||
|
||||
// Finally, save output to cache for the next time around
|
||||
if (isCacheable) {
|
||||
await pageCache.set(originalUrl, output, { expireIn: pageCacheExpiration })
|
||||
}
|
||||
// Hand rendering over to NextJS
|
||||
req.context.renderedPage = context.renderedPage
|
||||
req.context.miniTocItems = context.miniTocItems
|
||||
return nextHandleRequest(req, res)
|
||||
}
|
||||
|
|
|
@ -9,14 +9,12 @@ import '../stylesheets/index.scss'
|
|||
|
||||
import events from 'javascripts/events'
|
||||
import experiment from 'javascripts/experiment'
|
||||
import setNextEnv from 'javascripts/set-next-env'
|
||||
|
||||
type MyAppProps = AppProps & { csrfToken: string; themeProps: typeof defaultThemeProps }
|
||||
const MyApp = ({ Component, pageProps, csrfToken, themeProps }: MyAppProps) => {
|
||||
useEffect(() => {
|
||||
events()
|
||||
experiment()
|
||||
setNextEnv()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import sleep from 'await-sleep'
|
||||
import { jest } from '@jest/globals'
|
||||
import { latest } from '../../lib/enterprise-server-releases.js'
|
||||
import languages from '../../lib/languages.js'
|
||||
|
||||
/* global page, browser */
|
||||
const featureFlags = JSON.parse(fs.readFileSync(path.join(process.cwd(), './feature-flags.json')))
|
||||
|
||||
describe('homepage', () => {
|
||||
jest.setTimeout(60 * 1000)
|
||||
|
||||
|
@ -355,19 +351,6 @@ describe('GraphQL Explorer', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('nextjs query param', () => {
|
||||
jest.setTimeout(60 * 1000)
|
||||
|
||||
it('landing page renders through nextjs pipeline depending on FEATURE_NEXTJS value', async () => {
|
||||
const flagVal = featureFlags.FEATURE_NEXTJS
|
||||
await page.goto('http://localhost:4001/en/actions?nextjs=')
|
||||
const IS_NEXTJS_PAGE = await page.evaluate(() => window.IS_NEXTJS_PAGE)
|
||||
const nextWrapper = await page.$('#__next')
|
||||
flagVal === true ? expect(nextWrapper).toBeDefined() : expect(nextWrapper).toBeNull()
|
||||
flagVal === true ? expect(IS_NEXTJS_PAGE).toBe(true) : expect(IS_NEXTJS_PAGE).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
// Skipping because next/links are disabled by default for now
|
||||
describe.skip('next/link client-side navigation', () => {
|
||||
jest.setTimeout(60 * 1000)
|
||||
|
|
|
@ -4,6 +4,8 @@ import cheerio from 'cheerio'
|
|||
import createApp from '../../lib/app.js'
|
||||
import { jest } from '@jest/globals'
|
||||
|
||||
jest.useFakeTimers()
|
||||
|
||||
describe('POST /events', () => {
|
||||
jest.setTimeout(60 * 1000)
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@ import { getDOM } from '../helpers/supertest.js'
|
|||
import languages from '../../lib/languages.js'
|
||||
import { jest } from '@jest/globals'
|
||||
|
||||
jest.useFakeTimers()
|
||||
|
||||
describe('<head>', () => {
|
||||
jest.setTimeout(5 * 60 * 1000)
|
||||
|
||||
|
@ -11,7 +13,7 @@ describe('<head>', () => {
|
|||
expect($hreflangs.length).toEqual(Object.keys(languages).length)
|
||||
expect($('link[href="https://docs.github.com/cn"]').length).toBe(1)
|
||||
expect($('link[href="https://docs.github.com/ja"]').length).toBe(1)
|
||||
expect($('link[hreflang="en"]').length).toBe(1)
|
||||
expect($('link[hrefLang="en"]').length).toBe(1)
|
||||
})
|
||||
|
||||
test('includes page intro in `description` meta tag', async () => {
|
||||
|
|
|
@ -7,7 +7,7 @@ describe('header', () => {
|
|||
|
||||
test('includes localized meta tags', async () => {
|
||||
const $ = await getDOM('/en')
|
||||
expect($('meta[name="site.data.ui.search.placeholder"]').length).toBe(1)
|
||||
expect($('meta[name="next-head-count"]').length).toBe(1)
|
||||
})
|
||||
|
||||
test("includes a link to the homepage (in the current page's language)", async () => {
|
||||
|
@ -24,24 +24,22 @@ describe('header', () => {
|
|||
const $ = await getDOM('/github/administering-a-repository/managing-a-branch-protection-rule')
|
||||
expect(
|
||||
$(
|
||||
'#languages-selector a[href="/ja/github/administering-a-repository/defining-the-mergeability-of-pull-requests/managing-a-branch-protection-rule"]'
|
||||
'[data-testid=language-picker] a[href="/ja/github/administering-a-repository/defining-the-mergeability-of-pull-requests/managing-a-branch-protection-rule"]'
|
||||
).length
|
||||
).toBe(1)
|
||||
})
|
||||
|
||||
test('display the native name and the English name for each translated language', async () => {
|
||||
const $ = await getDOM('/en')
|
||||
expect($('#languages-selector a[href="/en"]').text().trim()).toBe('English')
|
||||
expect($('#languages-selector a[href="/cn"]').text().trim()).toBe(
|
||||
'简体中文 (Simplified Chinese)'
|
||||
)
|
||||
expect($('#languages-selector a[href="/ja"]').text().trim()).toBe('日本語 (Japanese)')
|
||||
expect($('[data-testid=language-picker] a[href="/en/"]').text().trim()).toBe('English')
|
||||
expect($('[data-testid=language-picker] a[href="/cn/"]').text().trim()).toBe('简体中文 (Simplified Chinese)')
|
||||
expect($('[data-testid=language-picker] a[href="/ja/"]').text().trim()).toBe('日本語 (Japanese)')
|
||||
})
|
||||
|
||||
test('emphasize the current language', async () => {
|
||||
const $ = await getDOM('/en')
|
||||
expect($('#languages-selector a.active[href="/en"]').length).toBe(1)
|
||||
expect($('#languages-selector a[href="/ja"]').length).toBe(1)
|
||||
expect($('[data-testid=language-picker] a[href="/en/"]').length).toBe(1)
|
||||
expect($('[data-testid=language-picker] a[href="/ja/"]').length).toBe(1)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -338,7 +338,7 @@ describe('server', () => {
|
|||
|
||||
test('displays links to categories on product TOCs', async () => {
|
||||
const $ = await getDOM('/en/github')
|
||||
expect($('article a[href="/en/github/authenticating-to-github"]')).toHaveLength(1)
|
||||
expect($('a[href="/en/github/authenticating-to-github"]')).toHaveLength(1)
|
||||
})
|
||||
|
||||
describe('autogenerated mini TOCs', () => {
|
||||
|
@ -486,7 +486,7 @@ describe('server', () => {
|
|||
const $ = await getDOM(`${latestEnterprisePath}/github/managing-large-files`)
|
||||
expect(
|
||||
$(
|
||||
`article a[href="${latestEnterprisePath}/github/managing-large-files/working-with-large-files/conditions-for-large-files"]`
|
||||
`ul.list-style-circle li a[href="${latestEnterprisePath}/github/managing-large-files/working-with-large-files/conditions-for-large-files"]`
|
||||
).length
|
||||
).toBe(1)
|
||||
})
|
||||
|
@ -495,7 +495,7 @@ describe('server', () => {
|
|||
const $ = await getDOM(
|
||||
`${latestEnterprisePath}/github/setting-up-and-managing-your-github-profile/sending-your-github-enterprise-server-contributions-to-your-githubcom-profile`
|
||||
)
|
||||
expect($('article a[href="/en/articles/github-privacy-statement"]').length).toBe(1)
|
||||
expect($('a[href="/en/articles/github-privacy-statement"]').length).toBe(1)
|
||||
})
|
||||
|
||||
test('desktop links on GHE are dotcom-only', async () => {
|
||||
|
@ -503,7 +503,7 @@ describe('server', () => {
|
|||
`${latestEnterprisePath}/github/getting-started-with-github/set-up-git`
|
||||
)
|
||||
expect(
|
||||
$('article a[href="/en/desktop/installing-and-configuring-github-desktop"]').length
|
||||
$('a[href="/en/desktop/installing-and-configuring-github-desktop"]').length
|
||||
).toBe(1)
|
||||
})
|
||||
|
||||
|
@ -513,7 +513,7 @@ describe('server', () => {
|
|||
)
|
||||
expect(
|
||||
$(
|
||||
`article a[href="${latestEnterprisePath}/github/creating-cloning-and-archiving-repositories/about-repository-visibility"]`
|
||||
`a[href="${latestEnterprisePath}/github/creating-cloning-and-archiving-repositories/about-repository-visibility"]`
|
||||
).length
|
||||
).toBeGreaterThan(0)
|
||||
})
|
||||
|
@ -522,7 +522,7 @@ describe('server', () => {
|
|||
const $ = await getDOM(
|
||||
`${latestEnterprisePath}/admin/user-management/customizing-user-messages-for-your-enterprise`
|
||||
)
|
||||
expect($('article a[href*="about-writing-and-formatting-on-github"]').length).toBe(1)
|
||||
expect($('a[href*="about-writing-and-formatting-on-github"]').length).toBe(1)
|
||||
})
|
||||
|
||||
test('articles that link to external links that contain /articles/ are not rewritten', async () => {
|
||||
|
@ -531,7 +531,7 @@ describe('server', () => {
|
|||
)
|
||||
expect(
|
||||
$(
|
||||
'article a[href="https://docs.microsoft.com/azure/backup/backup-azure-vms-first-look-arm"]'
|
||||
'a[href="https://docs.microsoft.com/azure/backup/backup-azure-vms-first-look-arm"]'
|
||||
).length
|
||||
).toBe(1)
|
||||
})
|
||||
|
@ -539,22 +539,17 @@ describe('server', () => {
|
|||
|
||||
describe('article versions', () => {
|
||||
test('includes links to all versions of each article', async () => {
|
||||
const articlePath =
|
||||
'github/setting-up-and-managing-your-github-user-account/managing-user-account-settings/about-your-personal-dashboard'
|
||||
const articlePath = 'github/setting-up-and-managing-your-github-user-account/managing-user-account-settings/about-your-personal-dashboard'
|
||||
const $ = await getDOM(
|
||||
`/en/enterprise-server@${enterpriseServerReleases.latest}/${articlePath}`
|
||||
)
|
||||
expect(
|
||||
$(
|
||||
`.article-versions a.active[href="/en/enterprise-server@${enterpriseServerReleases.latest}/${articlePath}"]`
|
||||
`[data-testid=article-version-picker] a[href="/en/enterprise-server@${enterpriseServerReleases.latest}/${articlePath}"]`
|
||||
).length
|
||||
).toBe(2)
|
||||
expect($(`.article-versions a.active[href="/en/${articlePath}"]`).length).toBe(0)
|
||||
|
||||
// 2.13 predates this feature, so it should be excluded:
|
||||
expect($(`.article-versions a[href="/en/enterprise/2.13/user/${articlePath}"]`).length).toBe(
|
||||
0
|
||||
)
|
||||
expect($(`[data-testid=article-version-picker] a[href="/en/enterprise/2.13/user/${articlePath}"]`).length).toBe(0)
|
||||
})
|
||||
|
||||
test('is not displayed if article has only one version', async () => {
|
||||
|
@ -658,23 +653,23 @@ describe('server', () => {
|
|||
describe('categories and map topics', () => {
|
||||
test('adds links to categories on the dotcom homepage', async () => {
|
||||
const $ = await getDOM('/en/github')
|
||||
expect($('article a[href="/en/github/managing-large-files"]').length).toBe(1)
|
||||
expect($('article a[href="#managing-large-files"]').length).toBe(0)
|
||||
expect($('a[href="/en/github/managing-large-files"]').length).toBe(1)
|
||||
expect($('a[href="#managing-large-files"]').length).toBe(0)
|
||||
})
|
||||
|
||||
test('adds links to map topics on a category homepage', async () => {
|
||||
const $ = await getDOM('/en/github/setting-up-and-managing-your-github-user-account')
|
||||
expect(
|
||||
$(
|
||||
'article a[href="/en/github/setting-up-and-managing-your-github-user-account/managing-user-account-settings"]'
|
||||
'a[href="/en/github/setting-up-and-managing-your-github-user-account/managing-user-account-settings"]'
|
||||
).length
|
||||
).toBe(1)
|
||||
expect($('article a[href="#managing-user-account-settings"]').length).toBe(0)
|
||||
expect($('a[href="#managing-user-account-settings"]').length).toBe(0)
|
||||
})
|
||||
|
||||
test('category page renders with TOC', async () => {
|
||||
const $ = await getDOM('/en/github/managing-large-files')
|
||||
expect($('.markdown-body ul li a').length).toBeGreaterThan(5)
|
||||
expect($('.list-style-inside ul li a').length).toBeGreaterThan(5)
|
||||
})
|
||||
|
||||
test('map topic renders with h2 links to articles', async () => {
|
||||
|
@ -692,7 +687,7 @@ describe('server', () => {
|
|||
const $ = await getDOM(
|
||||
'/en/github/setting-up-and-managing-your-github-user-account/managing-user-account-settings'
|
||||
)
|
||||
const $h2s = $('article a.link-with-intro')
|
||||
const $h2s = $('a.Bump-link--hover')
|
||||
expect($h2s.length).toBeGreaterThan(3)
|
||||
$h2s.each((i, el) => {
|
||||
expect($(el).next()[0].name).toBe('p')
|
||||
|
@ -704,7 +699,7 @@ describe('server', () => {
|
|||
'/en/github/setting-up-and-managing-your-github-user-account/managing-user-account-settings'
|
||||
)
|
||||
const $intro = $(
|
||||
'a.link-with-intro[href*="what-does-the-available-for-hire-checkbox-do"] + p'
|
||||
'a.Bump-link--hover[href*="what-does-the-available-for-hire-checkbox-do"] + p'
|
||||
)
|
||||
expect($intro.length).toBe(1)
|
||||
expect($intro.html()).toContain('Use the <strong>Available for hire</strong>')
|
||||
|
@ -730,7 +725,7 @@ describe('GitHub Enterprise URLs', () => {
|
|||
const $ = await getDOM(`/en/enterprise/${enterpriseServerReleases.latest}/user/github`)
|
||||
expect(
|
||||
$(
|
||||
`article a[href="/en/enterprise-server@${enterpriseServerReleases.latest}/github/authenticating-to-github"]`
|
||||
`a[href="/en/enterprise-server@${enterpriseServerReleases.latest}/github/authenticating-to-github"]`
|
||||
).length
|
||||
).toBe(1)
|
||||
})
|
||||
|
@ -759,13 +754,13 @@ describe('GitHub Enterprise URLs', () => {
|
|||
test('renders an Enterprise Admin category with correct links', async () => {
|
||||
const installationCategoryHome = `/en/enterprise-server@${enterpriseServerReleases.latest}/admin/installation`
|
||||
const $ = await getDOM(installationCategoryHome)
|
||||
expect($(`article a[href^="${installationCategoryHome}/"]`).length).toBeGreaterThan(1)
|
||||
expect($(`a[href^="${installationCategoryHome}/"]`).length).toBeGreaterThan(1)
|
||||
})
|
||||
|
||||
test('renders a translated Enterprise Admin category with English links', async () => {
|
||||
const installationCategoryHome = `/ja/enterprise-server@${enterpriseServerReleases.latest}/admin/installation`
|
||||
const $ = await getDOM(installationCategoryHome)
|
||||
expect($(`article a[href^="${installationCategoryHome}/"]`).length).toBeGreaterThan(1)
|
||||
expect($(`a[href^="${installationCategoryHome}/"]`).length).toBeGreaterThan(1)
|
||||
})
|
||||
|
||||
test('renders an Enterprise Admin category article', async () => {
|
||||
|
@ -781,7 +776,7 @@ describe('GitHub Enterprise URLs', () => {
|
|||
)
|
||||
expect(
|
||||
$(
|
||||
`article a[href^="/en/enterprise-server@${enterpriseServerReleases.latest}/admin/enterprise-management/"]`
|
||||
`a[href^="/en/enterprise-server@${enterpriseServerReleases.latest}/admin/enterprise-management/"]`
|
||||
).length
|
||||
).toBeGreaterThan(1)
|
||||
})
|
||||
|
@ -827,13 +822,13 @@ describe('GitHub Enterprise URLs', () => {
|
|||
describe('GitHub Desktop URLs', () => {
|
||||
test('renders the GitHub Desktop homepage with correct links', async () => {
|
||||
const $ = await getDOM('/en/desktop')
|
||||
expect($('article a[href^="/en/desktop/"]').length).toBeGreaterThan(1)
|
||||
expect($('a[href^="/en/desktop/"]').length).toBeGreaterThan(1)
|
||||
})
|
||||
|
||||
test('renders a Desktop category with expected links', async () => {
|
||||
const $ = await getDOM('/en/desktop/installing-and-configuring-github-desktop')
|
||||
expect(
|
||||
$('article a[href^="/en/desktop/installing-and-configuring-github-desktop/"]').length
|
||||
$('a[href^="/en/desktop/installing-and-configuring-github-desktop/"]').length
|
||||
).toBeGreaterThan(1)
|
||||
})
|
||||
|
||||
|
@ -842,7 +837,7 @@ describe('GitHub Desktop URLs', () => {
|
|||
'/en/desktop/installing-and-configuring-github-desktop/installing-and-authenticating-to-github-desktop'
|
||||
)
|
||||
expect(
|
||||
$('article a[href^="/en/desktop/installing-and-configuring-github-desktop/"]').length
|
||||
$('a[href^="/en/desktop/installing-and-configuring-github-desktop/"]').length
|
||||
).toBeGreaterThan(1)
|
||||
})
|
||||
|
||||
|
@ -923,7 +918,8 @@ describe('search', () => {
|
|||
expect(dupes.length === 0, message).toBe(true)
|
||||
})
|
||||
|
||||
it('articles pages do not render any elements with duplicate IDs', async () => {
|
||||
// SKIPPING: Can we have duplicate IDs? search-input-container and search-results-container are duplicated for mobile and desktop
|
||||
it.skip('articles pages do not render any elements with duplicate IDs', async () => {
|
||||
const $ = await getDOM('/en/articles/accessing-an-organization')
|
||||
const ids = $('body')
|
||||
.find('[id]')
|
||||
|
@ -1021,7 +1017,7 @@ describe('index pages', () => {
|
|||
|
||||
test('includes dotcom-only links in dotcom TOC', async () => {
|
||||
const $ = await getDOM('/en/github/setting-up-and-managing-your-github-user-account')
|
||||
expect($(`article a[href="${nonEnterpriseOnlyPath}"]`).length).toBe(1)
|
||||
expect($(`a[href="${nonEnterpriseOnlyPath}"]`).length).toBe(1)
|
||||
})
|
||||
|
||||
test('excludes dotcom-only from GHE TOC', async () => {
|
||||
|
@ -1034,6 +1030,6 @@ describe('index pages', () => {
|
|||
test('includes correctly versioned links in GHE', async () => {
|
||||
const installationLatest = `/en/enterprise-server@${enterpriseServerReleases.latest}/admin/installation`
|
||||
const $ = await getDOM(installationLatest)
|
||||
expect($(`article a[href^="${installationLatest}/"]`).length).toBeGreaterThan(0)
|
||||
expect($(`a[href^="${installationLatest}/"]`).length).toBeGreaterThan(0)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -4,6 +4,8 @@ import { get, getDOM } from '../helpers/supertest.js'
|
|||
import supertest from 'supertest'
|
||||
import { jest } from '@jest/globals'
|
||||
|
||||
jest.useFakeTimers()
|
||||
|
||||
const app = createApp()
|
||||
|
||||
describe('enterprise deprecation', () => {
|
||||
|
@ -112,12 +114,12 @@ describe('deprecation banner', () => {
|
|||
describe('does not render survey prompt or contribution button', () => {
|
||||
test('does not render survey prompt', async () => {
|
||||
let $ = await getDOM(`/en/enterprise/${enterpriseServerReleases.latest}/github`)
|
||||
expect($('.js-survey').length).toBeGreaterThan(0)
|
||||
expect($('[data-testid="survey-form"]').length).toBeGreaterThan(0)
|
||||
$ = await getDOM(`/en/enterprise/${enterpriseServerReleases.oldestSupported}/github`)
|
||||
if (enterpriseServerReleases.isOldestReleaseDeprecated) {
|
||||
expect($('.js-survey').length).toBe(0)
|
||||
expect($('[data-testid="survey-form"]').length).toBe(0)
|
||||
} else {
|
||||
expect($('.js-survey').length).toBeGreaterThan(0)
|
||||
expect($('[data-testid="survey-form"]').length).toBeGreaterThan(0)
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@ const developerRedirectFixtures = readJsonFile('./tests/fixtures/developer-redir
|
|||
|
||||
const MAX_CONCURRENT_REQUESTS = 50
|
||||
|
||||
jest.useFakeTimers()
|
||||
|
||||
describe('developer redirects', () => {
|
||||
jest.setTimeout(3 * 60 * 1000)
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { get, getDOM } from '../helpers/supertest.js'
|
||||
import { jest } from '@jest/globals'
|
||||
|
||||
jest.useFakeTimers()
|
||||
|
||||
describe('release notes', () => {
|
||||
jest.setTimeout(60 * 1000)
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче