-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: V0 Attachment compact component (#31634)
Co-authored-by: Juraj Kapsiar <jukapsia@microsoft.com>
- Loading branch information
1 parent
a8eba6a
commit ff2f5eb
Showing
20 changed files
with
780 additions
and
0 deletions.
There are no files selected for viewing
7 changes: 7 additions & 0 deletions
7
change/@fluentui-react-migration-v0-v9-08c554a0-6e37-421e-a3b4-2b50932aaaf9.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"type": "minor", | ||
"comment": "feat(v0-migration): Attachment compat component", | ||
"packageName": "@fluentui/react-migration-v0-v9", | ||
"email": "jukapsia@microsoft.com", | ||
"dependentChangeType": "patch" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
67 changes: 67 additions & 0 deletions
67
...ges/react-components/react-migration-v0-v9/src/components/Attachment/Attachment.styles.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { | ||
createCustomFocusIndicatorStyle, | ||
makeResetStyles, | ||
makeStyles, | ||
shorthands, | ||
tokens, | ||
} from '@fluentui/react-components'; | ||
import { attachmentActionClassName } from './AttachmentAction'; | ||
import { attachmentIconClassName } from './AttachmentIcon'; | ||
|
||
export const useAttachmentBaseStyles = makeResetStyles({ | ||
...createCustomFocusIndicatorStyle( | ||
{ | ||
outline: `${tokens.strokeWidthThick} solid ${tokens.colorStrokeFocus2}`, | ||
borderRadius: tokens.borderRadiusMedium, | ||
backgroundColor: undefined, | ||
color: undefined, | ||
[`& .${attachmentActionClassName}`]: { | ||
color: undefined, | ||
}, | ||
|
||
[`& .${attachmentIconClassName}`]: { | ||
color: undefined, | ||
}, | ||
}, | ||
{ selector: 'focus' }, | ||
), | ||
position: 'relative', | ||
display: 'inline-flex', | ||
alignItems: 'center', | ||
width: '100%', | ||
maxWidth: '424px', | ||
minHeight: '32px', | ||
...shorthands.padding('7px', '3px', '7px', '11px'), | ||
marginBottom: '2px', | ||
marginRight: '2px', | ||
backgroundColor: tokens.colorNeutralBackground6, | ||
color: tokens.colorNeutralForeground1, | ||
boxShadow: `0 .2rem .4rem -.075rem ${tokens.colorNeutralShadowAmbient}`, | ||
...shorthands.border('1px', 'solid', tokens.colorNeutralStroke3), | ||
borderRadius: '4px', | ||
}); | ||
|
||
export const useAttachmentStyles = makeStyles({ | ||
actionable: { | ||
cursor: 'pointer', | ||
':hover': { | ||
backgroundColor: tokens.colorNeutralBackground4Hover, | ||
}, | ||
}, | ||
progressContainer: { | ||
borderBottomLeftRadius: '4px', | ||
borderBottomRightRadius: '4px', | ||
bottom: 0, | ||
height: '4px', | ||
left: 0, | ||
overflow: 'hidden', | ||
position: 'absolute', | ||
right: 0, | ||
}, | ||
progressBar: { | ||
backgroundColor: tokens.colorPaletteLightGreenBackground3, | ||
height: '100%', | ||
maxWidth: '100%', | ||
transition: 'width 0.2s', | ||
}, | ||
}); |
50 changes: 50 additions & 0 deletions
50
...ages/react-components/react-migration-v0-v9/src/components/Attachment/Attachment.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import '@testing-library/jest-dom'; | ||
import { isConformant } from '@fluentui/react-conformance'; | ||
import * as React from 'react'; | ||
import { render, fireEvent } from '@testing-library/react'; | ||
import userEvent from '@testing-library/user-event'; | ||
|
||
import { Attachment } from './Attachment'; | ||
|
||
describe('Attachment', () => { | ||
isConformant({ | ||
Component: Attachment, | ||
componentPath: module!.filename.replace('.test', ''), | ||
displayName: 'Attachment', | ||
disabledTests: ['has-docblock', 'has-top-level-file', 'component-has-static-classnames-object'], | ||
}); | ||
|
||
it('renders a default state', () => { | ||
const { getByText } = render(<Attachment>Actionable</Attachment>); | ||
const textElement = getByText('Actionable'); | ||
expect(textElement.nodeName).toBe('DIV'); | ||
}); | ||
|
||
it('handles onClick', () => { | ||
const handleClick = jest.fn(); | ||
const { getByText } = render( | ||
<Attachment actionable onClick={handleClick}> | ||
Click me | ||
</Attachment>, | ||
); | ||
fireEvent.click(getByText('Click me')); | ||
expect(handleClick).toHaveBeenCalled(); | ||
}); | ||
|
||
it('handles Enter', () => { | ||
const handleClick = jest.fn(); | ||
const { getByText } = render( | ||
<Attachment actionable onClick={handleClick}> | ||
Click me | ||
</Attachment>, | ||
); | ||
userEvent.type(getByText('Click me'), '{enter}'); | ||
expect(handleClick).toHaveBeenCalled(); | ||
}); | ||
|
||
it('renders actionable', () => { | ||
const { getByText } = render(<Attachment actionable={true}>Actionable</Attachment>); | ||
const actionableElement = getByText('Actionable'); | ||
expect(actionableElement).toHaveAttribute('tabIndex', '0'); | ||
}); | ||
}); |
53 changes: 53 additions & 0 deletions
53
packages/react-components/react-migration-v0-v9/src/components/Attachment/Attachment.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { mergeClasses } from '@fluentui/react-components'; | ||
import { useARIAButtonProps } from '@fluentui/react-aria'; | ||
import * as React from 'react'; | ||
|
||
import { useAttachmentBaseStyles, useAttachmentStyles } from './Attachment.styles'; | ||
|
||
export const attachmentClassName = 'fui-Attachment'; | ||
export const attachmentProgressContainerClassName = `${attachmentClassName}__progress-container`; | ||
export const attachmentProgressBarClassName = `${attachmentClassName}__progress`; | ||
|
||
export interface AttachmentProps extends React.HTMLAttributes<HTMLElement> { | ||
actionable?: boolean; | ||
disabled?: boolean; | ||
progress?: string | number; | ||
onClick?: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void; | ||
} | ||
|
||
export const Attachment = React.forwardRef<HTMLDivElement, AttachmentProps>((props, ref) => { | ||
const { actionable, className, children, disabled, onClick, progress, onKeyDown, onKeyUp, ...rest } = props; | ||
const attachmentBaseClass = useAttachmentBaseStyles(); | ||
const classes = useAttachmentStyles(); | ||
|
||
const buttonProps = useARIAButtonProps('div', { | ||
disabled, | ||
onClick, | ||
onKeyDown: onKeyDown as React.KeyboardEventHandler<HTMLLIElement & HTMLDivElement>, | ||
onKeyUp: onKeyUp as React.KeyboardEventHandler<HTMLLIElement & HTMLDivElement>, | ||
}); | ||
|
||
return ( | ||
<div | ||
ref={ref} | ||
className={mergeClasses(attachmentClassName, attachmentBaseClass, actionable && classes.actionable, className)} | ||
{...(actionable && { | ||
'data-is-focusable': true, | ||
...buttonProps, | ||
})} | ||
{...rest} | ||
> | ||
{children} | ||
{!isNaN(Number(progress)) && ( | ||
<div className={mergeClasses(attachmentProgressContainerClassName, classes.progressContainer)}> | ||
<div | ||
className={mergeClasses(classes.progressBar, attachmentProgressBarClassName)} | ||
style={{ width: `${progress}%` }} | ||
/> | ||
</div> | ||
)} | ||
</div> | ||
); | ||
}); | ||
|
||
Attachment.displayName = 'Attachment'; |
18 changes: 18 additions & 0 deletions
18
...act-components/react-migration-v0-v9/src/components/Attachment/AttachmentAction.styles.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { makeStyles } from '@fluentui/react-components'; | ||
|
||
export const useAttachmentActionStyles = makeStyles({ | ||
root: { | ||
height: '32px', | ||
maxWidth: '280px', | ||
minWidth: '32px', | ||
display: 'inline-flex', | ||
justifyContent: 'center', | ||
alignItems: 'center', | ||
position: 'relative', | ||
verticalAlign: 'middle', | ||
cursor: 'pointer', | ||
}, | ||
disabled: { | ||
cursor: 'default', | ||
}, | ||
}); |
62 changes: 62 additions & 0 deletions
62
...eact-components/react-migration-v0-v9/src/components/Attachment/AttachmentAction.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import '@testing-library/jest-dom'; | ||
import * as React from 'react'; | ||
import { render, fireEvent } from '@testing-library/react'; | ||
import userEvent from '@testing-library/user-event'; | ||
|
||
import { AttachmentAction } from './AttachmentAction'; | ||
import { Attachment } from './Attachment'; | ||
|
||
describe('AttachmentAction', () => { | ||
it('renders a default state', () => { | ||
const { getByText } = render(<AttachmentAction>Action</AttachmentAction>); | ||
const textElement = getByText('Action'); | ||
expect(textElement.nodeName).toBe('BUTTON'); | ||
}); | ||
|
||
it('handles onClick', () => { | ||
const handleClick = jest.fn(); | ||
const { getByText } = render(<AttachmentAction onClick={handleClick}>Click me</AttachmentAction>); | ||
fireEvent.click(getByText('Click me')); | ||
expect(handleClick).toHaveBeenCalled(); | ||
}); | ||
|
||
it('handles Enter', () => { | ||
const handleClick = jest.fn(); | ||
const { getByText } = render(<AttachmentAction onClick={handleClick}>Click me</AttachmentAction>); | ||
userEvent.type(getByText('Click me'), '{enter}'); | ||
expect(handleClick).toHaveBeenCalled(); | ||
}); | ||
|
||
it('handles Enter when in Attachment', () => { | ||
const handleClick = jest.fn(); | ||
const handleAttachmentClick = jest.fn(); | ||
const { getByText } = render( | ||
<Attachment actionable onClick={handleAttachmentClick}> | ||
<AttachmentAction onClick={handleClick}>Click me</AttachmentAction> | ||
</Attachment>, | ||
); | ||
userEvent.type(getByText('Click me'), '{enter}'); | ||
expect(handleClick).toHaveBeenCalled(); | ||
expect(handleAttachmentClick).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('handles onKeyDown', () => { | ||
const handleKeyDown = jest.fn(); | ||
const { getByText } = render(<AttachmentAction onKeyDown={handleKeyDown}>Press key</AttachmentAction>); | ||
fireEvent.keyDown(getByText('Press key'), { key: 'Enter' }); | ||
expect(handleKeyDown).toHaveBeenCalled(); | ||
}); | ||
|
||
it('handles onKeyUp', () => { | ||
const handleKeyUp = jest.fn(); | ||
const { getByText } = render(<AttachmentAction onKeyUp={handleKeyUp}>Release key</AttachmentAction>); | ||
fireEvent.keyUp(getByText('Release key'), { key: 'Enter' }); | ||
expect(handleKeyUp).toHaveBeenCalled(); | ||
}); | ||
|
||
it('renders disabled', () => { | ||
const { getByText } = render(<AttachmentAction disabled={true}>Disabled</AttachmentAction>); | ||
const disabledElement = getByText('Disabled'); | ||
expect(disabledElement).toBeDisabled(); | ||
}); | ||
}); |
Oops, something went wrong.