Skip to content

Commit

Permalink
Action Bar - Experimental component (#4233)
Browse files Browse the repository at this point in the history
* Add new Actionbar component

* Add size context to ActionBar

* Add parent styles

* Get the overflow somewhat working

* Fix calculations

* Show More button appropriately

* Enhance overflow behaviour and add proper menu items

* Remove selection variant

* Add comment box story as example

* Add logic to show divider

* Add docs

* Fix builds

* Switch to drafts

* Remove stories from docs for now

* Update snapshots for exports

* Create old-peas-cross.md
  • Loading branch information
pksjce authored and lukasoppermann committed Apr 16, 2024
1 parent 63e2d1f commit d6f2d78
Show file tree
Hide file tree
Showing 8 changed files with 566 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/old-peas-cross.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/react": minor
---

Add a new experimental component ActionBar
8 changes: 4 additions & 4 deletions packages/react/src/UnderlineNav/UnderlineNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,16 @@ export type UnderlineNavProps = {
}
// When page is loaded, we don't have ref for the more button as it is not on the DOM yet.
// However, we need to calculate number of possible items when the more button present as well. So using the width of the more button as a constant.
const MORE_BTN_WIDTH = 86
export const MORE_BTN_WIDTH = 86
// The height is needed to make sure we don't have a layout shift when the more button is the only item in the nav.
const MORE_BTN_HEIGHT = 45

// Needed this because passing a ref using HTMLULListElement to `Box` causes a type error
const NavigationList = styled.ul`
export const NavigationList = styled.ul`
${sx};
`

const MoreMenuListItem = styled.li`
export const MoreMenuListItem = styled.li`
display: flex;
align-items: center;
height: ${MORE_BTN_HEIGHT}px;
Expand Down Expand Up @@ -113,7 +113,7 @@ const overflowEffect = (
updateListAndMenu({items, menuItems}, iconsVisible)
}

const getValidChildren = (children: React.ReactNode) => {
export const getValidChildren = (children: React.ReactNode) => {
return React.Children.toArray(children).filter(child => React.isValidElement(child)) as React.ReactElement[]
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ exports[`@primer/react/deprecated should not update exports without a semver cha

exports[`@primer/react/drafts should not update exports without a semver change 1`] = `
[
"type ActionBarProps",
"Blankslate",
"type BlankslateProps",
"callbackCancelledResult",
Expand All @@ -249,6 +250,7 @@ exports[`@primer/react/drafts should not update exports without a semver change
"type DataTableProps",
"default",
"default",
"default",
"Dialog",
"type DialogButtonProps",
"type DialogHeaderProps",
Expand Down Expand Up @@ -320,6 +322,7 @@ exports[`@primer/react/drafts should not update exports without a semver change

exports[`@primer/react/experimental should not update exports without a semver change 1`] = `
[
"type ActionBarProps",
"Blankslate",
"type BlankslateProps",
"callbackCancelledResult",
Expand All @@ -332,6 +335,7 @@ exports[`@primer/react/experimental should not update exports without a semver c
"type DataTableProps",
"default",
"default",
"default",
"Dialog",
"type DialogButtonProps",
"type DialogHeaderProps",
Expand Down
59 changes: 59 additions & 0 deletions packages/react/src/drafts/ActionBar/ActionBar.docs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"id": "actionbar",
"name": "ActionBar",
"status": "draft",
"a11yReviewed": false,
"stories": [],
"props": [
{
"name": "size",
"type": "'small' | 'medium' | 'large'",
"required": false,
"description": "Size of the action bar"
},
{
"name": "aria-label",
"type": "string",
"description": "When provided, a label is added to the action bar"
},
{
"name": "children",
"type": "React.ReactElement",
"required": true
}
],
"subcomponents": [
{
"name": "ActionBar.Icon",
"props": [
{
"name": "children",
"type": "React.ReactNode",
"defaultValue": "",
"required": true,
"description": "This will be the Button description."
},
{
"name": "icon",
"type": "Component",
"defaultValue": "",
"description": "provide an octicon. It will be placed in the center of the button"
},
{
"name": "aria-label",
"type": "string",
"defaultValue": "",
"description": "Use an aria label to describe the functionality of the button. Please refer to [our guidance on alt text](https://primer.style/guides/accessibility/alternative-text-for-images) for tips on writing good alternative text."
},
{
"name": "sx",
"type": "SystemStyleObject"
}
]
},
{
"name": "ActionBar.Divider",
"props": []
}
]
}
127 changes: 127 additions & 0 deletions packages/react/src/drafts/ActionBar/ActionBar.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import React from 'react'
import type {Meta} from '@storybook/react'
import ActionBar from '.'
import {
BoldIcon,
CodeIcon,
ItalicIcon,
SearchIcon,
LinkIcon,
FileAddedIcon,
HeadingIcon,
QuoteIcon,
ListUnorderedIcon,
ListOrderedIcon,
TasklistIcon,
} from '@primer/octicons-react'
import {MarkdownInput} from '../MarkdownEditor/_MarkdownInput'
import {ViewSwitch} from '../MarkdownEditor/_ViewSwitch'
import type {MarkdownViewMode} from '../MarkdownEditor/_ViewSwitch'
import {Box} from '../..'

export default {
title: 'Drafts/Components/ActionBar',
} as Meta<typeof ActionBar>

export const Default = () => (
<ActionBar>
<ActionBar.IconButton icon={BoldIcon} aria-label="Default"></ActionBar.IconButton>
<ActionBar.IconButton icon={ItalicIcon} aria-label="Default"></ActionBar.IconButton>
<ActionBar.IconButton icon={CodeIcon} aria-label="Default"></ActionBar.IconButton>
<ActionBar.IconButton icon={LinkIcon} aria-label="Default"></ActionBar.IconButton>
<ActionBar.Divider />
<ActionBar.IconButton icon={FileAddedIcon} aria-label="Default"></ActionBar.IconButton>
<ActionBar.IconButton icon={SearchIcon} aria-label="Default"></ActionBar.IconButton>
<ActionBar.IconButton icon={FileAddedIcon} aria-label="Default"></ActionBar.IconButton>
<ActionBar.IconButton icon={SearchIcon} aria-label="Default"></ActionBar.IconButton>
<ActionBar.IconButton icon={FileAddedIcon} aria-label="Default"></ActionBar.IconButton>
<ActionBar.IconButton icon={SearchIcon} aria-label="Default"></ActionBar.IconButton>
<ActionBar.IconButton icon={FileAddedIcon} aria-label="Default"></ActionBar.IconButton>
<ActionBar.IconButton icon={SearchIcon} aria-label="Default"></ActionBar.IconButton>
</ActionBar>
)

export const SmallActionBar = () => (
<ActionBar size="small">
<ActionBar.IconButton icon={BoldIcon} aria-label="Default"></ActionBar.IconButton>
<ActionBar.IconButton icon={ItalicIcon} aria-label="Default"></ActionBar.IconButton>
<ActionBar.IconButton icon={CodeIcon} aria-label="Default"></ActionBar.IconButton>
<ActionBar.IconButton icon={LinkIcon} aria-label="Default"></ActionBar.IconButton>
<ActionBar.Divider />
<ActionBar.IconButton icon={FileAddedIcon} aria-label="Default"></ActionBar.IconButton>
<ActionBar.IconButton icon={SearchIcon} aria-label="Default"></ActionBar.IconButton>
</ActionBar>
)

export const CommentBox = () => {
const [view, setView] = React.useState<MarkdownViewMode>('edit')
const loadPreview = React.useCallback(() => {
//console.log('loadPreview')
}, [])
const [value, setValue] = React.useState('')
return (
<Box
sx={{
maxWidth: 800,
display: 'flex',
flexDirection: 'column',
width: '100%',
borderColor: 'border.default',
borderWidth: 1,
borderStyle: 'solid',
borderRadius: 2,
minInlineSize: 'auto',
bg: 'canvas.default',
color: 'fg.default',
}}
>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
backgroundColor: 'canvas.subtle',
borderTopLeftRadius: 2,
borderTopRightRadius: 2,
justifyContent: 'space-between',
}}
as="header"
>
<Box sx={{width: '50%'}}>
<ViewSwitch
selectedView={view}
onViewSelect={setView}
//disabled={fileHandler?.uploadProgress !== undefined}
onLoadPreview={loadPreview}
/>
</Box>
<Box sx={{width: '50%'}}>
<ActionBar>
<ActionBar.IconButton icon={HeadingIcon} aria-label="Heading"></ActionBar.IconButton>
<ActionBar.IconButton icon={BoldIcon} aria-label="Bold"></ActionBar.IconButton>
<ActionBar.IconButton icon={ItalicIcon} aria-label="Italic"></ActionBar.IconButton>
<ActionBar.IconButton icon={CodeIcon} aria-label="Insert Code"></ActionBar.IconButton>
<ActionBar.IconButton icon={LinkIcon} aria-label="Insert Link"></ActionBar.IconButton>
<ActionBar.Divider />
<ActionBar.IconButton icon={QuoteIcon} aria-label="Insert Quote"></ActionBar.IconButton>
<ActionBar.IconButton icon={ListUnorderedIcon} aria-label="Unordered List"></ActionBar.IconButton>
<ActionBar.IconButton icon={ListOrderedIcon} aria-label="Ordered List"></ActionBar.IconButton>
<ActionBar.IconButton icon={TasklistIcon} aria-label="Task List"></ActionBar.IconButton>
</ActionBar>
</Box>
</Box>
<MarkdownInput
value={value}
visible={view === 'edit'}
onChange={e => {
setValue(e.target.value)
}}
id={'markdowninput'}
isDraggedOver={false}
minHeightLines={5}
maxHeightLines={35}
monospace={false}
pasteUrlsAsPlainText={false}
/>
</Box>
)
}
Loading

0 comments on commit d6f2d78

Please sign in to comment.