diff --git a/packages/react/src/BranchName/BranchName.features.stories.tsx b/packages/react/src/BranchName/BranchName.features.stories.tsx index 85db4bb601c..1c3f95c2a29 100644 --- a/packages/react/src/BranchName/BranchName.features.stories.tsx +++ b/packages/react/src/BranchName/BranchName.features.stories.tsx @@ -1,8 +1,16 @@ import type {Meta} from '@storybook/react-vite' +import React from 'react' import BranchName from './BranchName' import {Stack} from '../Stack' import Octicon from '../Octicon' -import {GitBranchIcon} from '@primer/octicons-react' +import {GitBranchIcon, CopyIcon, CheckIcon} from '@primer/octicons-react' +import {IconButton} from '../Button' +import {Tooltip} from '../TooltipV2' +import {VisuallyHidden} from '../VisuallyHidden' +import {Announce} from '../live-region' +import {clsx} from 'clsx' + +import styles from './BranchName.stories.module.css' export default { title: 'Components/BranchName/Features', @@ -28,3 +36,47 @@ export const LinkWithoutHref = () => ( ) export const NoProps = () => branch_name_no_props + +export const WithTrailingAction = ({ + branchName = 'fix/anchored-overlay-outside-top-autogrow', + repo = 'primer/react', +}: { + branchName: string + repo: string +}) => { + const [copied, setCopied] = React.useState(false) + + const handleCopy = () => { + setCopied(true) + void navigator.clipboard.writeText(branchName) + setTimeout(() => setCopied(false), 2000) + } + + const tooltipText = copied ? 'Copied!' : 'Copy branch name to clipboard' + + return ( + + + + {branchName} + + + {/* Screen reader announcement for copy success */} + {copied && ( + + Copied! + + )} + + + + + ) +} diff --git a/packages/react/src/BranchName/BranchName.stories.module.css b/packages/react/src/BranchName/BranchName.stories.module.css new file mode 100644 index 00000000000..2754c8e563e --- /dev/null +++ b/packages/react/src/BranchName/BranchName.stories.module.css @@ -0,0 +1,57 @@ +/* Story-only styles for demonstrating trailing action pattern */ + +.BranchNameWithTrailingAction { + display: inline-flex; + /* Let items stretch to fill container height */ + align-items: stretch; + /* Create room for pseudo-border divider */ + gap: var(--borderWidth-default); + flex-wrap: nowrap; + /* Moving the background to the container allows stacking the button hover background over it */ + background-color: var(--bgColor-accent-muted); + border-radius: var(--borderRadius-medium); + overflow: hidden; +} + +.BranchNameTransparent { + background-color: transparent; +} + +.TrailingActionButton { + /* Make the size auto calculated based on icon size and padding */ + height: auto; + width: auto; + padding: var(--base-size-2) var(--base-size-6); + position: relative; + /* Center content when button stretches to fill container */ + align-items: center; + + &:not(:disabled, [aria-disabled='true']) { + --button-invisible-iconColor-rest: var(--fgColor-link); + } + + &.TrailingActionButtonCopied:not(:disabled, [aria-disabled='true']) { + --button-invisible-iconColor-rest: var(--fgColor-success); + } + + [data-component='Octicon'] { + /* Set icon size to text size to exactly match the branch name component */ + height: var(--text-body-size-small); + width: var(--text-body-size-small); + } + + /* Dividing line between button and name. Using ::before on button allows for multiple buttons (with a line between each) */ + &::before { + content: ''; + position: absolute; + width: var(--borderWidth-default); + /* Position _between_ items (offsetting by width) to avoid overlapping with the real button border on hover/focus */ + inset: var(--base-size-4) auto var(--base-size-4) calc(-1 * var(--borderWidth-default)); + /* stylelint-disable-next-line primer/colors */ + background-color: var(--borderColor-accent-muted); + } +} + +.TrailingActionButtonCopied { + /* Class exists for JS toggling - styles handled via compound selector above */ +}