diff --git a/.changeset/blue-fireants-behave.md b/.changeset/blue-fireants-behave.md new file mode 100644 index 00000000000..7f763372b41 --- /dev/null +++ b/.changeset/blue-fireants-behave.md @@ -0,0 +1,5 @@ +--- +'@primer/react': minor +--- + +PageHeader: Draft implementation diff --git a/src/PageHeader/PageHeader.stories.tsx b/src/PageHeader/PageHeader.stories.tsx new file mode 100644 index 00000000000..c781473185d --- /dev/null +++ b/src/PageHeader/PageHeader.stories.tsx @@ -0,0 +1,284 @@ +import React from 'react' +import {Meta, Story} from '@storybook/react' +import {Button, IconButton, Breadcrumbs, Link, Text, StateLabel, BranchName, Box} from '..' +import {UnderlineNav} from '../UnderlineNav2' +import Label from '../Label' +import { + KebabHorizontalIcon, + GitBranchIcon, + CodeIcon, + GitPullRequestIcon, + PeopleIcon, + PencilIcon, + CommentDiscussionIcon, + CommitIcon, + ChecklistIcon, + FileDiffIcon, + ArrowRightIcon, + SidebarExpandIcon, +} from '@primer/octicons-react' +import {OcticonArgType} from '../utils/story-helpers' + +import {PageHeader} from './PageHeader' +import Hidden from '../Hidden' + +const meta: Meta = { + title: 'Drafts/Components/PageHeader', + parameters: { + layout: 'fullscreen', + controls: {expanded: true}, + }, + args: { + hasContextArea: false, + hasParentLink: true, + ParentLink: 'Previous page', + hasContextBar: false, + hasContextAreaAction: true, + hasLeadingAction: false, + hasTitle: true, + Title: 'Branches', + 'Title.as': 'h2', + 'Title.variant': 'medium', + hasLeadingVisual: false, + LeadingVisual: GitBranchIcon, + hasTrailingVisual: false, + hasTrailingAction: false, + hasActions: false, + hasDescription: false, + hasNavigation: false, + }, + argTypes: { + hasContextArea: { + type: 'boolean', + table: { + category: 'ContextArea Slot', + type: {summary: 'string'}, + }, + description: + 'ContextArea is only visible on narrow viewports by default to provide user context of where they are at their journey.', + }, + ParentLink: { + type: 'string', + if: {arg: 'hasContextArea'}, + table: { + category: 'ContextArea Slot', + }, + description: 'The default way to let users navigate up in the hierarchy on Narrow viewports.', + }, + hasParentLink: { + type: 'boolean', + if: {arg: 'hasContextArea'}, + table: { + category: 'ContextArea Slot', + }, + description: 'Parent ', + }, + hasContextBar: { + type: 'boolean', + if: {arg: 'hasContextArea'}, + table: { + category: 'ContextArea Slot', + }, + description: + 'ContextBar is generic slot for any component above the title region. Use it for custom breadcrumbs and other navigation elements instead of ParentLink.', + }, + hasContextAreaAction: { + type: 'boolean', + if: {arg: 'hasContextArea'}, + table: { + category: 'ContextArea Slot', + }, + }, + hasLeadingAction: { + type: 'boolean', + table: { + category: 'TitleArea Slot', + }, + description: + 'A back button can be used as a leading action for local navigation. On Narrow viewports, use parentLink instead.', + }, + hasTitle: { + type: 'boolean', + table: { + category: 'TitleArea Slot', + }, + description: + 'ContextBar is generic slot for any component above the title region. Use it for custom breadcrumbs and other navigation elements instead of ParentLink.', + }, + Title: { + type: 'string', + table: { + category: 'TitleArea Slot', + }, + }, + 'Title.as': { + control: { + type: 'select', + }, + options: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'], + table: { + category: 'TitleArea Slot', + }, + }, + 'Title.variant': { + control: { + type: 'radio', + }, + options: ['large', 'medium', 'subtitle'], + table: { + category: 'TitleArea Slot', + }, + description: + '`medium` is the most common page title size. Use for static titles in most situations. `large` for for user-generated content such as issues, pull requests, or discussions. `subtitle` when a PageHeader.Title is already present in the page, such as in a SplitPageLayout.', + }, + hasLeadingVisual: { + type: 'boolean', + table: { + category: 'TitleArea Slot', + }, + }, + LeadingVisual: { + ...OcticonArgType([CodeIcon, GitPullRequestIcon, PeopleIcon]), + table: { + category: 'TitleArea Slot', + }, + description: + 'Leading visualLeading visuals are optional and appear at the start of the title. They can be octicons, avatars, and other custom visuals that fit a small area.', + }, + hasTrailingVisual: { + type: 'boolean', + table: { + category: 'TitleArea Slot', + }, + description: + 'Trailing visualTrailing visual and trailing text can display auxiliary information. They are placed at the right of the item, and can denote status, privacy details, etc.', + }, + hasTrailingAction: { + type: 'boolean', + table: { + category: 'TitleArea Slot', + }, + }, + hasActions: { + type: 'boolean', + table: { + category: 'TitleArea Slot', + }, + description: 'Description region/slot', + }, + hasDescription: { + type: 'boolean', + table: { + category: 'Other Slots', + }, + description: 'Description region/slot', + }, + hasNavigation: { + type: 'boolean', + table: { + category: 'Other Slots', + }, + description: 'Description region/slot', + }, + }, +} + +const Template: Story = args => ( + + + + + + + + + + + + + + + +) + +export const Playground = Template.bind({}) + +export default meta diff --git a/src/PageHeader/PageHeader.test.tsx b/src/PageHeader/PageHeader.test.tsx new file mode 100644 index 00000000000..81d726637a2 --- /dev/null +++ b/src/PageHeader/PageHeader.test.tsx @@ -0,0 +1,159 @@ +import React from 'react' +import '@testing-library/jest-dom/extend-expect' +import {render} from '@testing-library/react' +import {PageHeader} from '.' +import MatchMediaMock from 'jest-matchmedia-mock' +import {behavesAsComponent, checkExports, checkStoriesForAxeViolations} from '../utils/testing' +import {act} from 'react-test-renderer' +import {viewportRanges} from '../hooks/useResponsiveValue' +import {IconButton} from '../Button' +import {ChevronLeftIcon, GitBranchIcon, PencilIcon, SidebarExpandIcon} from '@primer/octicons-react' + +let matchmedia: MatchMediaMock +describe('PageHeader', () => { + beforeAll(() => { + matchmedia = new MatchMediaMock() + }) + afterAll(() => { + matchmedia.clear() + }) + behavesAsComponent({ + Component: PageHeader, + options: {skipAs: true, skipSx: true}, + toRender: () => ( + + + + + + + ), + }) + checkExports('PageHeader', { + default: undefined, + PageHeader, + }) + it('renders default layout', () => { + const {container} = render( + + ContextArea + TitleArea + Description + Navigation + , + ) + expect(container).toMatchSnapshot() + }) + it('does not render ContextArea in wide viewport as default', () => { + act(() => { + matchmedia.useMediaQuery(viewportRanges.wide) + }) + + const {getByText} = render( + + ContextArea + TitleArea + Description + Navigation + , + ) + expect(getByText('ContextArea')).not.toBeVisible() + }) + it('respects the hidden prop of ContextArea and renders accordingly', () => { + act(() => { + matchmedia.useMediaQuery(viewportRanges.regular) + }) + + const {getByText} = render( + + + TitleArea + Description + Navigation + , + ) + expect(getByText('ContextArea')).toBeVisible() + }) + it('respects default visibility of LeadingAction and TrailingAction and renders accordingly', () => { + act(() => { + matchmedia.useMediaQuery(viewportRanges.narrow) + }) + const {getByTestId} = render( + + ContextArea + + + + + Title + + + + + + + , + ) + expect(getByTestId('LeadingAction')).not.toBeVisible() + expect(getByTestId('TrailingAction')).not.toBeVisible() + }) + it('respects the title variant prop', () => { + act(() => { + matchmedia.useMediaQuery(viewportRanges.narrow) + }) + const {getByText} = render( + + ContextArea + + Title + + , + ) + expect(getByText('Title')).toHaveStyle('font-size: 2rem') + }) + it("respects the title variant prop and updates the children components' container height accordingly", () => { + act(() => { + matchmedia.useMediaQuery(viewportRanges.narrow) + }) + const {getByText} = render( + + ContextArea + + + Leading Action + + + + Leading Visual + + + Title + + Trailing Action + + + + Trailing Visual + + + + , + ) + + expect(getByText('Leading Visual')).toHaveStyle('height: 3rem') + expect(getByText('Trailing Visual')).toHaveStyle('height: 3rem') + expect(getByText('Leading Action')).toHaveStyle('height: 3rem') + expect(getByText('Trailing Action')).toHaveStyle('height: 3rem') + // add actions here + }) +}) + +checkStoriesForAxeViolations('examples', '../PageHeader/') diff --git a/src/PageHeader/PageHeader.tsx b/src/PageHeader/PageHeader.tsx new file mode 100644 index 00000000000..4fdc2d705b6 --- /dev/null +++ b/src/PageHeader/PageHeader.tsx @@ -0,0 +1,393 @@ +import React from 'react' +import {Box} from '..' +import {useResponsiveValue, ResponsiveValue} from '../hooks/useResponsiveValue' +import {SxProp, merge, BetterSystemStyleObject} from '../sx' +import Heading from '../Heading' +import {ArrowLeftIcon} from '@primer/octicons-react' +import Link from '../Link' +import {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' +const REGION_ORDER = { + ContextArea: 0, + TitleArea: 1, + Description: 2, + Navigation: 3, +} + +// Types that are shared between sub components +export type sharedPropTypes = { + hidden?: boolean | ResponsiveValue +} & SxProp + +// Default state for the `visible` prop when a sub component is only visible on narrow viewport +const hiddenOnRegularAndWide = { + narrow: false, + regular: true, + wide: true, +} + +// Default state for the `visible` prop when a sub component is visible on regular and wide viewport +const hiddenOnNarrow = { + narrow: true, + regular: false, + wide: false, +} + +// Root +// ----------------------------------------------------------------------------- +export type PageHeaderProps = { + 'aria-label'?: React.AriaAttributes['aria-label'] + as?: React.ElementType | 'header' | 'div' +} & sharedPropTypes + +const Root: React.FC> = ({children, sx = {}, as = 'div'}) => { + const rootStyles = { + display: 'flex', + flexDirection: 'column', + // TODO: We used hard-coded values for the spacing and font size in this component. Update them to use new design tokens when they are ready to use. + gap: '0.5rem', + } + return ( + (rootStyles, sx)}> + {children} + + ) +} + +// PageHeader.ContextArea : Only visible on narrow viewports by default to provide user context of where they are at their journey. `visible` prop available +// to manage their custom visibility but consumers should be careful if they choose to hide this on narrow viewports. +// PageHeader.ContextArea Sub Components: PageHeader.ParentLink, PageHeader.ContextBar, PageHeader.ContextAreaActions +// --------------------------------------------------------------------- + +const ContextArea: React.FC> = ({ + children, + hidden = hiddenOnRegularAndWide, + sx = {}, +}) => { + const isHidden = useResponsiveValue(hidden, false) + const contentNavStyles = { + display: isHidden ? 'none' : 'flex', + flexDirection: 'row', + alignItems: 'center', + gap: '0.5rem', + order: REGION_ORDER.ContextArea, + } + return (contentNavStyles, sx)}>{children} +} +type LinkProps = Pick< + React.AnchorHTMLAttributes, + 'download' | 'href' | 'hrefLang' | 'media' | 'ping' | 'rel' | 'target' | 'type' | 'referrerPolicy' +> +export type ParentLinkProps = PageHeaderProps & LinkProps + +const ParentLink = React.forwardRef( + ( + { + children, + sx = {}, + href, + 'aria-label': ariaLabel = `Back to ${children}`, + as = 'a', + hidden = hiddenOnRegularAndWide, + }, + ref, + ) => { + const isHidden = useResponsiveValue(hidden, false) + return ( + <> + ( + { + display: isHidden ? 'none' : 'flex', + alignItems: 'center', + gap: '0.5rem', + }, + sx, + )} + href={href} + > + + {children} + + + ) + }, +) as PolymorphicForwardRefComponent<'a', ParentLinkProps> + +// ContextBar +// Generic slot for any component above the title region. Use it for custom breadcrumbs and other navigation elements instead of ParentLink. +// --------------------------------------------------------------------- + +const ContextBar: React.FC> = ({ + children, + sx = {}, + hidden = hiddenOnRegularAndWide, +}) => { + const isHidden = useResponsiveValue(hidden, false) + return ({display: isHidden ? 'none' : 'flex'}, sx)}>{children} +} + +// ContextAreaActions +// --------------------------------------------------------------------- +const ContextAreaActions: React.FC> = ({ + children, + sx = {}, + hidden = hiddenOnRegularAndWide, +}) => { + const isHidden = useResponsiveValue(hidden, false) + return ( + ( + { + display: isHidden ? 'none' : 'flex', + flexDirection: 'row', + alignItems: 'center', + gap: '0.5rem', + flexGrow: '1', + justifyContent: 'right', + }, + sx, + )} + > + {children} + + ) +} + +const MEDIUM_TITLE_HEIGHT = '2rem' +const LARGE_TITLE_HEIGHT = '3rem' + +const TitleAreaContext = React.createContext<{ + titleVariant: 'subtitle' | 'medium' | 'large' + titleAreaHeight?: string | number +}>({ + titleVariant: 'medium', + titleAreaHeight: MEDIUM_TITLE_HEIGHT, +}) + +type TitleAreaProps = { + variant?: 'subtitle' | 'medium' | 'large' | ResponsiveValue<'subtitle' | 'medium' | 'large'> +} & PageHeaderProps +// PageHeader.TitleArea: The main title area of the page. Visible on all viewports. +// PageHeader.TitleArea Sub Components: PageHeader.LeadingAction, PageHeader.LeadingVisual, PageHeader.Title, PageTitle.TrailingVisual, PageHeader.TrailingAction, PageHeader.Actions +// PageHeader.LeadingAction and PageHeader.TrailingAction are only visible on regular viewports therefore they come as visible on narrow viewports and their visibility can be managed by their exposed `visible` prop +// --------------------------------------------------------------------- + +const TitleArea: React.FC> = ({ + children, + sx = {}, + hidden = false, + variant = 'medium', +}) => { + const isHidden = useResponsiveValue(hidden, false) + const currentVariant = useResponsiveValue(variant, 'medium') + const height = currentVariant === 'large' ? LARGE_TITLE_HEIGHT : MEDIUM_TITLE_HEIGHT + return ( + + ( + {gap: '0.5rem', display: isHidden ? 'none' : 'flex', flexDirection: 'row', alignItems: 'flex-start'}, + sx, + )} + > + {children} + + + ) +} + +const LeadingAction: React.FC> = ({ + children, + sx = {}, + hidden = hiddenOnNarrow, +}) => { + const isHidden = useResponsiveValue(hidden, false) + const {titleAreaHeight} = React.useContext(TitleAreaContext) + + return ( + ( + {display: isHidden ? 'none' : 'flex', alignItems: 'center', height: titleAreaHeight}, + sx, + )} + > + {children} + + ) +} + +const LeadingVisual: React.FC> = ({children, sx = {}, hidden = false}) => { + const isHidden = useResponsiveValue(hidden, false) + const {titleAreaHeight} = React.useContext(TitleAreaContext) + return ( + ( + { + display: isHidden ? 'none' : 'flex', + alignItems: 'center', + height: titleAreaHeight, + }, + sx, + )} + > + {children} + + ) +} + +export type TitleProps = { + // Check if we need responsive values for heading is so should we update as prop's type for Heading component? + as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' +} & PageHeaderProps + +const Title: React.FC> = ({children, sx = {}, hidden = false, as = 'h3'}) => { + const isHidden = useResponsiveValue(hidden, false) + const {titleVariant} = React.useContext(TitleAreaContext) + return ( + ( + { + fontSize: { + large: '2rem', + medium: '1.25rem', + subtitle: '1.25rem', + }[titleVariant], + // line-height is calculated with calc(height/font-size) and the below numbers are from @primer/primitives + lineHeight: { + large: 1.5, // calc(48/32) + medium: 1.6, // calc(32/20) + subtitle: 1.6, // calc(32/20) + }[titleVariant], + fontWeight: { + large: '400', + medium: '600', + subtitle: '400', + }[titleVariant], + display: isHidden ? 'none' : 'flex', + }, + sx, + )} + > + {children} + + ) +} +const TrailingVisual: React.FC> = ({children, sx = {}, hidden = false}) => { + const isHidden = useResponsiveValue(hidden, false) + const {titleAreaHeight} = React.useContext(TitleAreaContext) + + return ( + ( + { + display: isHidden ? 'none' : 'flex', + alignItems: 'center', + height: titleAreaHeight, + }, + sx, + )} + > + {children} + + ) +} + +const TrailingAction: React.FC> = ({ + children, + sx = {}, + hidden = hiddenOnNarrow, +}) => { + const isHidden = useResponsiveValue(hidden, false) + const {titleAreaHeight} = React.useContext(TitleAreaContext) + + return ( + ( + {display: isHidden ? 'none' : 'flex', alignItems: 'center', height: titleAreaHeight}, + sx, + )} + > + {children} + + ) +} + +const Actions: React.FC> = ({children, sx = {}, hidden = false}) => { + const isHidden = useResponsiveValue(hidden, false) + const {titleAreaHeight} = React.useContext(TitleAreaContext) + return ( + ( + { + display: isHidden ? 'none' : 'flex', + flexDirection: 'row', + gap: '0.5rem', + flexGrow: '1', + justifyContent: 'right', + height: titleAreaHeight, + alignItems: 'center', + }, + sx, + )} + > + {children} + + ) +} + +// PageHeader.Description: The description area of the header. Visible on all viewports +const Description: React.FC> = ({children, sx = {}, hidden = false}) => { + const isHidden = useResponsiveValue(hidden, true) + return ( + ( + { + display: isHidden ? 'none' : 'flex', + flexDirection: 'row', + alignItems: 'center', + gap: '0.5rem', + }, + sx, + )} + > + {children} + + ) +} + +// PageHeader.Navigation: The local navigation area of the header. Visible on all viewports +const Navigation: React.FC> = ({children, sx = {}, hidden = false}) => { + const isHidden = useResponsiveValue(hidden, false) + return ( + ( + { + display: isHidden ? 'none' : 'block', + }, + sx, + )} + > + {children} + + ) +} + +export const PageHeader = Object.assign(Root, { + ContextArea, + ParentLink, + ContextBar, + ContextAreaActions, + TitleArea, + LeadingAction, + LeadingVisual, + Title, + TrailingVisual, + TrailingAction, + Actions, + Description, + Navigation, +}) diff --git a/src/PageHeader/__snapshots__/PageHeader.test.tsx.snap b/src/PageHeader/__snapshots__/PageHeader.test.tsx.snap new file mode 100644 index 00000000000..c21d48ac2da --- /dev/null +++ b/src/PageHeader/__snapshots__/PageHeader.test.tsx.snap @@ -0,0 +1,175 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PageHeader renders consistently 1`] = ` +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + gap: 0.5rem; +} + +.c1 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + gap: 0.5rem; + -webkit-order: 0; + -ms-flex-order: 0; + order: 0; +} + +.c2 { + gap: 0.5rem; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: flex-start; + -webkit-box-align: flex-start; + -ms-flex-align: flex-start; + align-items: flex-start; +} + +.c3 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + gap: 0.5rem; +} + +.c4 { + display: block; +} + +
+
+
+
+
+
+`; + +exports[`PageHeader renders default layout 1`] = ` +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + gap: 0.5rem; +} + +.c1 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + gap: 0.5rem; + -webkit-order: 0; + -ms-flex-order: 0; + order: 0; +} + +.c2 { + gap: 0.5rem; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: flex-start; + -webkit-box-align: flex-start; + -ms-flex-align: flex-start; + align-items: flex-start; +} + +.c3 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + gap: 0.5rem; +} + +.c4 { + display: block; +} + +
+
+
+ ContextArea +
+
+ TitleArea +
+
+ Description +
+
+ Navigation +
+
+
+`; diff --git a/src/PageHeader/examples.stories.tsx b/src/PageHeader/examples.stories.tsx new file mode 100644 index 00000000000..f84411de157 --- /dev/null +++ b/src/PageHeader/examples.stories.tsx @@ -0,0 +1,211 @@ +import React from 'react' +import {Meta} from '@storybook/react' +import {Button, IconButton, Breadcrumbs, Link, Text, StateLabel, BranchName, Box} from '..' +import { + KebabHorizontalIcon, + GitBranchIcon, + CodeIcon, + CommentDiscussionIcon, + CommitIcon, + ChecklistIcon, + FileDiffIcon, + ArrowRightIcon, +} from '@primer/octicons-react' + +import {PageHeader} from './PageHeader' +import Hidden from '../Hidden' +import {UnderlineNav} from '../UnderlineNav2' + +const meta: Meta = { + title: 'Drafts/Components/PageHeader/Examples', + parameters: { + layout: 'fullscreen', + controls: {expanded: true}, + }, + args: {}, +} + +// See if there is an interest to take this into global params + +const PrimerViewports = { + xsmall: { + name: 'Xsmall', + styles: { + width: '320px', + height: '100%', + }, + }, + small: { + name: 'Small', + styles: { + width: '540px', + height: '100%', + }, + }, + medium: { + name: 'Medium', + styles: { + width: '768px', + height: '100%', + }, + }, + large: { + name: 'Large', + styles: { + width: '1012px', + height: '100%', + }, + }, + xlarge: { + name: 'Xlarge', + styles: { + width: '1280px', + height: '100%', + }, + }, + xxlarge: { + name: 'XXlarge', + styles: { + width: '1400px', + height: '100%', + }, + }, +} + +const setViewportParamToNarrow = { + viewport: { + viewports: { + ...PrimerViewports, + }, + defaultViewport: 'small', + }, +} +export const Webhooks = () => ( + + + + Repository settings + + + Webhooks + + + + + + + + + + + + + +) + +export const WebhooksOnNarrowViewport = () => { + return +} + +WebhooksOnNarrowViewport.parameters = setViewportParamToNarrow + +export const PullRequestPage = () => ( + + + + Pull requests + + + + PageHeader component initial layout explorations extra long pull request title + + + + + {/* Pop up actions */} + + + + + + + + + + Open + + + + broccolinisoup + {' '} + wants to merge 3 commits into main from{' '} + broccolinisoup/switch-to-new-underlineNav + + + + + main + + page-header-initial + + + + + + + Conversation + + + Commits + + + Checks + + + Files Changes + + + + + +) + +export const PullRequestPageOnNarrowViewport = () => { + return +} + +PullRequestPageOnNarrowViewport.parameters = setViewportParamToNarrow + +export const FilesPage = () => ( + + + + Files + + + + + + + + ... + primer + react + src + PageHeader + PageHeader.tsx + + + + +) + +export const FilesPageOnNarrowViewport = () => { + return +} + +FilesPageOnNarrowViewport.parameters = setViewportParamToNarrow + +export default meta diff --git a/src/PageHeader/features.stories.tsx b/src/PageHeader/features.stories.tsx new file mode 100644 index 00000000000..ccd9c89a351 --- /dev/null +++ b/src/PageHeader/features.stories.tsx @@ -0,0 +1,137 @@ +import React from 'react' +import {Meta} from '@storybook/react' +import {IconButton, ActionMenu, ActionList, Box} from '..' +import {PencilIcon, KebabHorizontalIcon, ArchiveIcon, TrashIcon} from '@primer/octicons-react' + +import {PageHeader} from './PageHeader' + +const meta: Meta = { + title: 'Drafts/Components/PageHeader/Features', + parameters: { + layout: 'fullscreen', + controls: {expanded: true}, + }, + args: {}, +} + +const PrimerViewports = { + xsmall: { + name: 'Xsmall', + styles: { + width: '320px', + height: '100%', + }, + }, + small: { + name: 'Small', + styles: { + width: '540px', + height: '100%', + }, + }, + medium: { + name: 'Medium', + styles: { + width: '768px', + height: '100%', + }, + }, + large: { + name: 'Large', + styles: { + width: '1012px', + height: '100%', + }, + }, + xlarge: { + name: 'Xlarge', + styles: { + width: '1280px', + height: '100%', + }, + }, + xxlarge: { + name: 'XXlarge', + styles: { + width: '1400px', + height: '100%', + }, + }, +} + +export const TitleWithTrailingAction = () => ( + + + + Projects + + + Primer Backlog + + + + + + + + +) + +TitleWithTrailingAction.parameters = { + viewport: { + viewports: { + ...PrimerViewports, + }, + defaultViewport: 'medium', + }, +} + +export const TitleWithTrailingActionOnNarrowViewports = () => { + return +} + +TitleWithTrailingActionOnNarrowViewports.parameters = { + viewport: { + viewports: { + ...PrimerViewports, + }, + defaultViewport: 'small', + }, +} + +export default meta diff --git a/src/PageHeader/index.ts b/src/PageHeader/index.ts new file mode 100644 index 00000000000..3e3ee9ffef7 --- /dev/null +++ b/src/PageHeader/index.ts @@ -0,0 +1 @@ +export * from './PageHeader'