-
Notifications
You must be signed in to change notification settings - Fork 2.9k
feat(react-headless-components-preview): add headless Overflow #36305
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mainframev
wants to merge
4
commits into
microsoft:master
Choose a base branch
from
mainframev:feat/headless-overflow
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
76233ad
feat(react-headless-components-preview): add headless Overflow component
mainframev 747370f
fix: address fixes after review
mainframev 684bdf6
fixup! fix: address fixes after review
mainframev bc12e7f
fixup! fix: address fixes after review
mainframev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
7 changes: 7 additions & 0 deletions
7
change/@fluentui-react-headless-components-preview-4bdc7f70-88bc-4b7a-8c35-7ddd600b9a3c.json
This file contains hidden or 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": "patch", | ||
| "comment": "feat: add headless Overflow component", | ||
| "packageName": "@fluentui/react-headless-components-preview", | ||
| "email": "vgenaev@gmail.com", | ||
| "dependentChangeType": "patch" | ||
| } | ||
This file contains hidden or 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
89 changes: 89 additions & 0 deletions
89
.../react-components/react-headless-components-preview/library/etc/overflow.api.md
This file contains hidden or 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,89 @@ | ||
| ## API Report File for "@fluentui/react-headless-components-preview" | ||
|
|
||
| > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). | ||
|
|
||
| ```ts | ||
|
|
||
| import { DATA_OVERFLOW_DIVIDER } from '@fluentui/react-overflow'; | ||
| import { DATA_OVERFLOW_ITEM } from '@fluentui/react-overflow'; | ||
| import { DATA_OVERFLOW_MENU } from '@fluentui/react-overflow'; | ||
| import { DATA_OVERFLOWING } from '@fluentui/react-overflow'; | ||
| import type { ForwardRefComponent } from '@fluentui/react-utilities'; | ||
| import { OnOverflowChangeData } from '@fluentui/react-overflow'; | ||
| import { OverflowComponentState } from '@fluentui/react-overflow'; | ||
| import { OverflowContextValues } from '@fluentui/react-overflow'; | ||
| import { OverflowDivider } from '@fluentui/react-overflow'; | ||
| import { OverflowDividerProps } from '@fluentui/react-overflow'; | ||
| import { OverflowItem } from '@fluentui/react-overflow'; | ||
| import { OverflowItemProps } from '@fluentui/react-overflow'; | ||
| import { OverflowProps } from '@fluentui/react-overflow'; | ||
| import { OverflowReorderObserver } from '@fluentui/react-overflow'; | ||
| import { OverflowState } from '@fluentui/react-overflow'; | ||
| import { renderOverflow_unstable as renderOverflow } from '@fluentui/react-overflow'; | ||
| import { useIsOverflowGroupVisible } from '@fluentui/react-overflow'; | ||
| import { useIsOverflowItemVisible } from '@fluentui/react-overflow'; | ||
| import { useOverflow_unstable as useOverflow } from '@fluentui/react-overflow'; | ||
| import { useOverflowContext } from '@fluentui/react-overflow'; | ||
| import { useOverflowContextValues_unstable as useOverflowContextValues } from '@fluentui/react-overflow'; | ||
| import { useOverflowCount } from '@fluentui/react-overflow'; | ||
| import { useOverflowDivider } from '@fluentui/react-overflow'; | ||
| import { useOverflowItem } from '@fluentui/react-overflow'; | ||
| import { useOverflowMenu } from '@fluentui/react-overflow'; | ||
| import { useOverflowVisibility } from '@fluentui/react-overflow'; | ||
|
|
||
| export { DATA_OVERFLOW_DIVIDER } | ||
|
|
||
| export { DATA_OVERFLOW_ITEM } | ||
|
|
||
| export { DATA_OVERFLOW_MENU } | ||
|
|
||
| export { DATA_OVERFLOWING } | ||
|
|
||
| export { OnOverflowChangeData } | ||
|
|
||
| // @public | ||
| export const Overflow: ForwardRefComponent<OverflowProps>; | ||
|
|
||
| export { OverflowComponentState } | ||
|
|
||
| export { OverflowContextValues } | ||
|
|
||
| export { OverflowDivider } | ||
|
|
||
| export { OverflowDividerProps } | ||
|
|
||
| export { OverflowItem } | ||
|
|
||
| export { OverflowItemProps } | ||
|
|
||
| export { OverflowProps } | ||
|
|
||
| export { OverflowReorderObserver } | ||
|
|
||
| export { OverflowState } | ||
|
|
||
| export { renderOverflow } | ||
|
|
||
| export { useIsOverflowGroupVisible } | ||
|
|
||
| export { useIsOverflowItemVisible } | ||
|
|
||
| export { useOverflow } | ||
|
|
||
| export { useOverflowContext } | ||
|
|
||
| export { useOverflowContextValues } | ||
|
|
||
| export { useOverflowCount } | ||
|
|
||
| export { useOverflowDivider } | ||
|
|
||
| export { useOverflowItem } | ||
|
|
||
| export { useOverflowMenu } | ||
|
|
||
| export { useOverflowVisibility } | ||
|
|
||
| // (No @packageDocumentation comment for this package) | ||
|
|
||
| ``` |
This file contains hidden or 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
77 changes: 77 additions & 0 deletions
77
...nents/react-headless-components-preview/library/src/components/Overflow/Overflow.test.tsx
This file contains hidden or 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,77 @@ | ||
| import * as React from 'react'; | ||
| import { render } from '@testing-library/react'; | ||
| import { isConformant } from '../../testing/isConformant'; | ||
| import { Overflow } from './Overflow'; | ||
| import { OverflowItem, useOverflowMenu } from './index'; | ||
|
|
||
| describe('Overflow', () => { | ||
| isConformant({ | ||
| Component: Overflow, | ||
| displayName: 'Overflow', | ||
| // Overflow is renderless (clones its single child) and provides context, so the slot/render | ||
| // based conformance assertions do not apply. | ||
| disabledTests: ['component-handles-ref', 'component-has-root-ref', 'component-handles-classname'], | ||
| requiredProps: { children: <div /> } as object, | ||
| }); | ||
|
|
||
| it('forwards the ref to the single child', () => { | ||
| const overflowRef = jest.fn(); | ||
| const childRef = jest.fn(); | ||
|
|
||
| render( | ||
| <Overflow ref={overflowRef}> | ||
| <div id="child" ref={childRef} /> | ||
| </Overflow>, | ||
| ); | ||
|
|
||
| expect(overflowRef).toHaveBeenCalledWith(expect.objectContaining({ id: 'child', tagName: 'DIV' })); | ||
| expect(childRef).toHaveBeenCalledWith(expect.objectContaining({ id: 'child', tagName: 'DIV' })); | ||
| }); | ||
|
|
||
| it('does not add any built-in className to the cloned child', () => { | ||
| const { container } = render( | ||
| <Overflow> | ||
| <div className="my-container" /> | ||
| </Overflow>, | ||
| ); | ||
|
|
||
| // The headless Overflow must preserve only the child's own className — no Griffel/fui-* classes. | ||
| expect((container.firstChild as HTMLElement).className).toBe('my-container'); | ||
| }); | ||
|
|
||
| it('registers overflow items on the child element', () => { | ||
| const { getByText } = render( | ||
| <Overflow> | ||
| <div> | ||
| <OverflowItem id="1"> | ||
| <button>foo</button> | ||
| </OverflowItem> | ||
| </div> | ||
| </Overflow>, | ||
| ); | ||
|
|
||
| expect(getByText('foo')).toHaveAttribute('data-overflow-item'); | ||
| }); | ||
|
|
||
| it('marks the overflow menu element with data-overflow-menu for styling', () => { | ||
| const Menu: React.FC = () => { | ||
| const { ref } = useOverflowMenu<HTMLButtonElement>(); | ||
| return ( | ||
| <button ref={ref} data-testid="menu"> | ||
| menu | ||
| </button> | ||
| ); | ||
| }; | ||
|
|
||
| const { getByTestId } = render( | ||
| <Overflow> | ||
| <div> | ||
| <Menu /> | ||
| </div> | ||
| </Overflow>, | ||
| ); | ||
|
|
||
| // Headless: the engine sets the data attribute; consumers style it (e.g. `flex-shrink: 0`). | ||
| expect(getByTestId('menu')).toHaveAttribute('data-overflow-menu'); | ||
| }); | ||
| }); |
20 changes: 20 additions & 0 deletions
20
...components/react-headless-components-preview/library/src/components/Overflow/Overflow.tsx
This file contains hidden or 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,20 @@ | ||
| 'use client'; | ||
|
|
||
| import * as React from 'react'; | ||
| import type { ForwardRefComponent } from '@fluentui/react-utilities'; | ||
| import type { OverflowProps } from './Overflow.types'; | ||
| import { useOverflow } from './useOverflow'; | ||
| import { useOverflowContextValues } from './useOverflowContextValues'; | ||
| import { renderOverflow } from './renderOverflow'; | ||
|
|
||
| /** | ||
| * Provides an overflow context for `OverflowItem` descendants without any built-in styling. | ||
| */ | ||
| export const Overflow: ForwardRefComponent<OverflowProps> = React.forwardRef((props, ref) => { | ||
| const state = useOverflow(props, ref); | ||
| const contextValues = useOverflowContextValues(state); | ||
|
|
||
| return renderOverflow(state, contextValues); | ||
| }); | ||
|
|
||
| Overflow.displayName = 'Overflow'; |
9 changes: 9 additions & 0 deletions
9
...nents/react-headless-components-preview/library/src/components/Overflow/Overflow.types.ts
This file contains hidden or 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,9 @@ | ||
| export type { | ||
| OverflowProps, | ||
| OnOverflowChangeData, | ||
| OverflowItemProps, | ||
| OverflowDividerProps, | ||
| OverflowState, | ||
| OverflowComponentState, | ||
| OverflowContextValues, | ||
| } from '@fluentui/react-overflow'; |
31 changes: 31 additions & 0 deletions
31
...act-components/react-headless-components-preview/library/src/components/Overflow/index.ts
This file contains hidden or 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,31 @@ | ||
| export { Overflow } from './Overflow'; | ||
| export { useOverflow } from './useOverflow'; | ||
| export { useOverflowContextValues } from './useOverflowContextValues'; | ||
| export { renderOverflow } from './renderOverflow'; | ||
| export type { | ||
| OverflowProps, | ||
| OverflowState, | ||
| OverflowComponentState, | ||
| OverflowContextValues, | ||
| OnOverflowChangeData, | ||
| OverflowItemProps, | ||
| OverflowDividerProps, | ||
| } from './Overflow.types'; | ||
|
|
||
| export { | ||
| OverflowItem, | ||
| OverflowDivider, | ||
| OverflowReorderObserver, | ||
| useOverflowMenu, | ||
| useOverflowContext, | ||
| useOverflowCount, | ||
| useOverflowItem, | ||
| useOverflowDivider, | ||
| useIsOverflowItemVisible, | ||
| useIsOverflowGroupVisible, | ||
| useOverflowVisibility, | ||
| DATA_OVERFLOWING, | ||
| DATA_OVERFLOW_ITEM, | ||
| DATA_OVERFLOW_MENU, | ||
| DATA_OVERFLOW_DIVIDER, | ||
| } from '@fluentui/react-overflow'; |
1 change: 1 addition & 0 deletions
1
...nents/react-headless-components-preview/library/src/components/Overflow/renderOverflow.ts
This file contains hidden or 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 @@ | ||
| export { renderOverflow_unstable as renderOverflow } from '@fluentui/react-overflow'; |
1 change: 1 addition & 0 deletions
1
...mponents/react-headless-components-preview/library/src/components/Overflow/useOverflow.ts
This file contains hidden or 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 @@ | ||
| export { useOverflow_unstable as useOverflow } from '@fluentui/react-overflow'; |
1 change: 1 addition & 0 deletions
1
...t-headless-components-preview/library/src/components/Overflow/useOverflowContextValues.ts
This file contains hidden or 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 @@ | ||
| export { useOverflowContextValues_unstable as useOverflowContextValues } from '@fluentui/react-overflow'; |
30 changes: 30 additions & 0 deletions
30
packages/react-components/react-headless-components-preview/library/src/overflow.ts
This file contains hidden or 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,30 @@ | ||
| export { | ||
| Overflow, | ||
| useOverflow, | ||
| useOverflowContextValues, | ||
| renderOverflow, | ||
| useOverflowMenu, | ||
| OverflowItem, | ||
| OverflowDivider, | ||
| OverflowReorderObserver, | ||
| useOverflowContext, | ||
| useOverflowCount, | ||
| useOverflowItem, | ||
| useOverflowDivider, | ||
| useIsOverflowItemVisible, | ||
| useIsOverflowGroupVisible, | ||
| useOverflowVisibility, | ||
| DATA_OVERFLOWING, | ||
| DATA_OVERFLOW_ITEM, | ||
| DATA_OVERFLOW_MENU, | ||
| DATA_OVERFLOW_DIVIDER, | ||
| } from './components/Overflow'; | ||
| export type { | ||
| OverflowProps, | ||
| OverflowState, | ||
| OverflowComponentState, | ||
| OverflowContextValues, | ||
| OnOverflowChangeData, | ||
| OverflowItemProps, | ||
| OverflowDividerProps, | ||
| } from './components/Overflow'; |
67 changes: 67 additions & 0 deletions
67
...onents/react-headless-components-preview/stories/src/Overflow/OverflowDefault.stories.tsx
This file contains hidden or 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 * as React from 'react'; | ||
| import { | ||
| Overflow, | ||
| OverflowItem, | ||
| useOverflowMenu, | ||
| useIsOverflowItemVisible, | ||
| } from '@fluentui/react-headless-components-preview/overflow'; | ||
| import { Menu, MenuTrigger, MenuPopover, MenuList, MenuItem } from '@fluentui/react-headless-components-preview/menu'; | ||
|
|
||
| import styles from './overflow.module.css'; | ||
|
|
||
| const itemIds = Array.from({ length: 10 }, (_, i) => (i + 1).toString()); | ||
|
|
||
| const OverflowMenuItem = ({ id }: { id: string }): React.ReactNode => { | ||
| // Only the overflowed (hidden) items are listed in the menu. | ||
| const isVisible = useIsOverflowItemVisible(id); | ||
| return isVisible ? null : <MenuItem className={styles.menuItem}>Item {id}</MenuItem>; | ||
| }; | ||
|
|
||
| /** | ||
| * `+N` button that opens a headless `Menu` listing the overflowed items. `MenuPopover` renders | ||
| * through a portal but preserves React context, so the overflow hooks inside still read the | ||
| * `Overflow` root's context. | ||
| */ | ||
| const OverflowMenu = ({ ids }: { ids: string[] }): React.ReactNode => { | ||
| const { ref, overflowCount, isOverflowing } = useOverflowMenu<HTMLButtonElement>(); | ||
|
|
||
| if (!isOverflowing) { | ||
| return null; | ||
| } | ||
|
|
||
| return ( | ||
| <Menu> | ||
| <MenuTrigger> | ||
| <button ref={ref} type="button" className={styles.menu} aria-label={`${overflowCount} more items`}> | ||
| +{overflowCount} | ||
| </button> | ||
| </MenuTrigger> | ||
| <MenuPopover className={styles.menuPopover}> | ||
| <MenuList className={styles.menuList}> | ||
| {ids.map(id => ( | ||
| <OverflowMenuItem key={id} id={id} /> | ||
| ))} | ||
| </MenuList> | ||
| </MenuPopover> | ||
| </Menu> | ||
| ); | ||
| }; | ||
|
|
||
| /** | ||
| * Drag the dashed box's right edge to resize. Items that no longer fit are hidden and the `+N` | ||
| * button reflects the overflow count; click it to see the overflowed items. | ||
| */ | ||
| export const Default = (): React.ReactNode => ( | ||
| <div className={styles.resizer}> | ||
| <Overflow> | ||
| <div className={styles.container}> | ||
| {itemIds.map(id => ( | ||
| <OverflowItem key={id} id={id}> | ||
| <button className={styles.item}>Item {id}</button> | ||
| </OverflowItem> | ||
| ))} | ||
| <OverflowMenu ids={itemIds} /> | ||
| </div> | ||
| </Overflow> | ||
| </div> | ||
| ); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🕵🏾♀️ visual changes to review in the Visual Change Report
vr-tests-react-components/Avatar Converged 1 screenshots
vr-tests-react-components/Charts-DonutChart 1 screenshots
vr-tests-react-components/Menu Converged - submenuIndicator slotted content 1 screenshots
vr-tests-react-components/Positioning 2 screenshots
vr-tests-react-components/ProgressBar converged 3 screenshots
vr-tests-react-components/TagPicker 1 screenshots
There were 3 duplicate changes discarded. Check the build logs for more information.