1
0
mirror of synced 2025-12-19 18:10:59 -05:00

Permissions redesign (#48873)

This commit is contained in:
Rachael Sewell
2024-02-20 14:36:36 -08:00
committed by GitHub
parent 664c779482
commit 4cd427842a
14 changed files with 100 additions and 67 deletions

View File

@@ -47,7 +47,7 @@ pages:
miniToc: In this article miniToc: In this article
all_enterprise_releases: All Enterprise Server releases all_enterprise_releases: All Enterprise Server releases
about_versions: About versions about_versions: About versions
permissions_statement: Who can use this feature permissions_callout_title: Who can use this feature?
video_from_transcript: See video for this transcript video_from_transcript: See video for this transcript
support: support:
still_need_help: Still need help? still_need_help: Still need help?

View File

@@ -7,7 +7,6 @@ import { ArticleGridLayout } from 'src/frame/components/article/ArticleGridLayou
import { MiniTocs } from 'src/frame/components/ui/MiniTocs' import { MiniTocs } from 'src/frame/components/ui/MiniTocs'
import { useAutomatedPageContext } from 'src/automated-pipelines/components/AutomatedPageContext' import { useAutomatedPageContext } from 'src/automated-pipelines/components/AutomatedPageContext'
import { ClientSideHighlight } from 'src/frame/components/ClientSideHighlight' import { ClientSideHighlight } from 'src/frame/components/ClientSideHighlight'
import { Alert } from 'src/frame/components/ui/Alert'
import { Breadcrumbs } from 'src/frame/components/page-header/Breadcrumbs' import { Breadcrumbs } from 'src/frame/components/page-header/Breadcrumbs'
type Props = { type Props = {
@@ -40,9 +39,7 @@ export const AutomatedPage = ({ children }: Props) => {
</Lead> </Lead>
)} )}
{permissions && <PermissionsStatement permissions={permissions} />} <PermissionsStatement permissions={permissions} product={product} />
{product && <Alert className="mb-4" html={product} />}
</> </>
} }
toc={miniTocItems.length > 1 && <MiniTocs miniTocItems={miniTocItems} />} toc={miniTocItems.length > 1 && <MiniTocs miniTocItems={miniTocItems} />}

View File

@@ -15,6 +15,7 @@ children:
- /for-playwright - /for-playwright
- /html-short-title - /html-short-title
- /page-with-callout - /page-with-callout
- /page-with-permissions-and-product-callout
- /table-with-ifversions - /table-with-ifversions
- /code-snippet-with-hashbang - /code-snippet-with-hashbang
--- ---

View File

@@ -0,0 +1,17 @@
---
title: Page with permissions and product callout
shortTitle: Callout page
intro: A very short intro
permissions: This is a permission callout
product: '{% data reusables.gated-features.not-for-ghes %}'
versions:
fpt: '*'
ghes: '*'
ghec: '*'
---
## Heading
Note that this page uses the `product` and `permissions` frontmatter property. So it should
result in a call out box rendered with two messages. But only if the version is *not*
Enterprise Server.

View File

@@ -47,7 +47,7 @@ pages:
miniToc: In this article miniToc: In this article
all_enterprise_releases: All Enterprise Server releases all_enterprise_releases: All Enterprise Server releases
about_versions: About versions about_versions: About versions
permissions_statement: Who can use this feature permissions_callout_title: Who can use this feature?
video_from_transcript: See video for this transcript video_from_transcript: See video for this transcript
support: support:
still_need_help: Still need help? still_need_help: Still need help?

View File

@@ -1,26 +0,0 @@
import { getDOM } from '#src/tests/helpers/e2etest.js'
describe('alerts', () => {
test('article page', async () => {
const $ = await getDOM('/get-started/foo/page-with-callout')
const callout = $('[data-testid=alert] div')
expect(callout.html()).toBe('<p>Callout for HubGit Pages</p>')
})
test('callout disappears depend on Liquid inside it', async () => {
// This page has `product:` property which is a piece of Liquid
// which makes it so that the rendered output of that becomes
// an empty string.
// This test tests that alert is not rendered if its output
// "exits" but is empty.
const $ = await getDOM('/enterprise-server@latest/get-started/foo/page-with-callout')
const callout = $('[data-testid=alert]')
expect(callout.length).toBe(0)
})
test('toc landing page', async () => {
const $ = await getDOM('/actions/category')
const callout = $('[data-testid=alert] div')
expect(callout.html()).toBe('<p>This is the callout text</p>')
})
})

View File

@@ -8,17 +8,6 @@ describe('markdown rendering', () => {
expect(html).toMatch('<code>syntax</code>') expect(html).toMatch('<code>syntax</code>')
expect(html).toMatch('<em>HubGit</em>') expect(html).toMatch('<em>HubGit</em>')
}) })
test('page with permission frontmatter', async () => {
const $ = await getDOM('/get-started/markdown/permissions')
const html = $('[data-testid="permissions-statement"]').html()
// part of the UI
expect(html).toMatch('Who can use this feature')
// Markdown
expect(html).toMatch('<strong>admin</strong>')
// Liquid
expect(html).toMatch('HubGit Pages site')
})
}) })
describe('alerts', () => { describe('alerts', () => {

View File

@@ -0,0 +1,48 @@
import { getDOM } from '#src/tests/helpers/e2etest.js'
describe('permission statements', () => {
test('article page product statement', async () => {
const $ = await getDOM('/get-started/foo/page-with-callout')
const callout = $('[data-testid=product-statement] div')
expect(callout.html()).toBe('<p>Callout for HubGit Pages</p>')
})
test('callout disappears depend on Liquid inside it', async () => {
// This page has `product:` property which is a piece of Liquid
// which makes it so that the rendered output of that becomes
// an empty string.
// This test tests that alert is not rendered if its output
// "exits" but is empty.
const $ = await getDOM('/enterprise-server@latest/get-started/foo/page-with-callout')
const callout = $('[data-testid=product-statement]')
expect(callout.length).toBe(0)
})
test('toc landing page', async () => {
const $ = await getDOM('/actions/category')
const callout = $('[data-testid=product-statement] div')
expect(callout.html()).toBe('<p>This is the callout text</p>')
})
test('page with permission frontmatter', async () => {
const $ = await getDOM('/get-started/markdown/permissions')
const html = $('[data-testid=permissions-statement] div').html()
// Markdown
expect(html).toMatch('<strong>admin</strong>')
// Liquid
expect(html).toMatch('HubGit Pages site')
})
test('page with permission frontmatter and product statement', async () => {
const $ = await getDOM('/get-started/foo/page-with-permissions-and-product-callout.md')
const html = $('[data-testid=permissions-callout] div').html()
// part of the UI
expect(html).toMatch('Who can use this feature')
const permission = $('[data-testid=permissions-statement] div')
expect(permission.html()).toBe('<p>This is a permission callout</p>')
const product = $('[data-testid=product-statement] div')
expect(product.html()).toBe('<p>Callout for HubGit Pages</p>')
})
})

View File

@@ -25,7 +25,7 @@ export const ArticleGridLayout = ({
<Box className={cx(styles.containerBox, className)}> <Box className={cx(styles.containerBox, className)}>
{topper && <Box gridArea="topper">{topper}</Box>} {topper && <Box gridArea="topper">{topper}</Box>}
{intro && ( {intro && (
<Box id="article-intro" gridArea="intro" className="f4"> <Box id="article-intro" gridArea="intro" className="f4 pb-4">
{intro} {intro}
</Box> </Box>
)} )}

View File

@@ -3,7 +3,6 @@ import dynamic from 'next/dynamic'
import cx from 'classnames' import cx from 'classnames'
import { LinkExternalIcon } from '@primer/octicons-react' import { LinkExternalIcon } from '@primer/octicons-react'
import { Alert } from 'src/frame/components/ui/Alert'
import { DefaultLayout } from 'src/frame/components/DefaultLayout' import { DefaultLayout } from 'src/frame/components/DefaultLayout'
import { ArticleTitle } from 'src/frame/components/article/ArticleTitle' import { ArticleTitle } from 'src/frame/components/article/ArticleTitle'
import { useArticleContext } from 'src/frame/components/context/ArticleContext' import { useArticleContext } from 'src/frame/components/context/ArticleContext'
@@ -62,12 +61,10 @@ export const ArticlePage = () => {
const introCalloutsProp = ( const introCalloutsProp = (
<> <>
{permissions && <PermissionsStatement permissions={permissions} />} <PermissionsStatement permissions={permissions} product={product} />
{includesPlatformSpecificContent && <PlatformPicker />} {includesPlatformSpecificContent && <PlatformPicker />}
{includesToolSpecificContent && <ToolPicker />} {includesToolSpecificContent && <ToolPicker />}
{product && <Alert className="mb-4" html={product} />}
</> </>
) )

View File

@@ -1,3 +0,0 @@
.permissions_statement {
border-left: 0.25rem solid var(--color-border-muted);
}

View File

@@ -1,22 +1,35 @@
import cx from 'classnames' import { Box } from '@primer/react'
import { PersonIcon, BriefcaseIcon } from '@primer/octicons-react'
import styles from './PermissionsStatement.module.scss'
import { useTranslation } from 'src/languages/components/useTranslation' import { useTranslation } from 'src/languages/components/useTranslation'
type Props = { type Props = {
permissions: string product?: string
permissions?: string
} }
export function PermissionsStatement({ permissions }: Props) { export function PermissionsStatement({ product, permissions }: Props) {
const { t } = useTranslation('pages') const { t } = useTranslation('pages')
if (!permissions && !product) return null
return ( return (
<div <Box sx={{ borderRadius: 10, borderStyle: 'solid', borderColor: 'border.default', p: 3 }}>
className={cx(styles.permissions_statement, 'pl-3 my-4')} <div data-search="hide" data-testid="permissions-callout">
data-search="hide" <div className="mb-3 d-inline-block">
data-testid="permissions-statement" <h2 className="f4">{t('permissions_callout_title')}</h2>
> </div>
<div className="text-bold pr-2">{t('permissions_statement')}</div> {permissions && (
<div dangerouslySetInnerHTML={{ __html: permissions }} /> <div className="d-flex" data-testid="permissions-statement">
</div> <PersonIcon className="mt-1" />
<div className="pl-2" dangerouslySetInnerHTML={{ __html: permissions }} />
</div>
)}
{product && (
<div className="d-flex" data-testid="product-statement">
<BriefcaseIcon className="mt-1" />
<div className="pl-2" dangerouslySetInnerHTML={{ __html: product }} />
</div>
)}
</div>
</Box>
) )
} }

View File

@@ -9,7 +9,7 @@ import { ArticleTitle } from 'src/frame/components/article/ArticleTitle'
import { MarkdownContent } from 'src/frame/components/ui/MarkdownContent' import { MarkdownContent } from 'src/frame/components/ui/MarkdownContent'
import { ArticleList } from 'src/landings/components/ArticleList' import { ArticleList } from 'src/landings/components/ArticleList'
import { ArticleGridLayout } from 'src/frame/components/article/ArticleGridLayout' import { ArticleGridLayout } from 'src/frame/components/article/ArticleGridLayout'
import { Alert } from 'src/frame/components/ui/Alert' import { PermissionsStatement } from 'src/frame/components/ui/PermissionsStatement'
import { Lead } from 'src/frame/components/ui/Lead' import { Lead } from 'src/frame/components/ui/Lead'
import { LearningTrackNav } from 'src/learning-track/components/article/LearningTrackNav' import { LearningTrackNav } from 'src/learning-track/components/article/LearningTrackNav'
import { ClientSideRedirects } from 'src/rest/components/ClientSideRedirects' import { ClientSideRedirects } from 'src/rest/components/ClientSideRedirects'
@@ -46,7 +46,7 @@ export const TocLanding = () => {
{intro && <Lead data-search="lead">{intro}</Lead>} {intro && <Lead data-search="lead">{intro}</Lead>}
{productCallout && <Alert html={productCallout} />} <PermissionsStatement product={productCallout} />
<div className="border-bottom border-xl-0 pb-4 mb-5 pb-xl-2 mb-xl-2" /> <div className="border-bottom border-xl-0 pb-4 mb-5 pb-xl-2 mb-xl-2" />

View File

@@ -16,7 +16,7 @@ export type StructuredContentT = {
} }
export const RestReferencePage = ({ restOperations }: StructuredContentT) => { export const RestReferencePage = ({ restOperations }: StructuredContentT) => {
const { title, intro, renderedPage, permissions } = useAutomatedPageContext() const { title, intro, renderedPage, permissions, product } = useAutomatedPageContext()
// Scrollable code blocks in our REST API docs and elsewhere aren't accessible // Scrollable code blocks in our REST API docs and elsewhere aren't accessible
// via keyboard navigation without setting tabindex="0". But we don't want to set // via keyboard navigation without setting tabindex="0". But we don't want to set
@@ -54,7 +54,7 @@ export const RestReferencePage = ({ restOperations }: StructuredContentT) => {
</Lead> </Lead>
)} )}
{permissions && <PermissionsStatement permissions={permissions} />} <PermissionsStatement permissions={permissions} product={product} />
{renderedPage && <MarkdownContent className="pt-3 pb-4">{renderedPage}</MarkdownContent>} {renderedPage && <MarkdownContent className="pt-3 pb-4">{renderedPage}</MarkdownContent>}
{restOperations.length > 0 && ( {restOperations.length > 0 && (