Skip to content

Commit

Permalink
Merge branch 'master' into feat/drawer-motion
Browse files Browse the repository at this point in the history
* master:
  docs(react-drawer): best practices (microsoft#28040)
  docs(react-drawer): add missing documentation for Drawer stories (microsoft#28284)
  docs(react-drawer): improve type descriptions and fix TS circular references (microsoft#28282)
  docs(react-drawer): improve drawer stories examples (microsoft#28283)
  bugfix: adds grid-template-columns to DialogBody styles to ensure grid template layout (microsoft#28272)
  Doc: Tree Infinite Scrolling (microsoft#28197)
  fix(react-card): infer a11y id from immediate header element (microsoft#28266)
  Fixed bugs and added more stories to the Breadcrumb (microsoft#28267)
  refactor: Keep vanillajs code only where needed (microsoft#28278)
  fix: correcting focus behavior of react-search (microsoft#28241)
  Tooltip : updated tooltip styles (microsoft#28264)
  • Loading branch information
marcosmoura committed Jun 22, 2023
2 parents 38e7bc6 + fd49c13 commit d2df033
Show file tree
Hide file tree
Showing 59 changed files with 1,073 additions and 403 deletions.
14 changes: 14 additions & 0 deletions apps/vr-tests-react-components/src/stories/Tooltip.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,20 @@ storiesOf('Tooltip Converged', module)
<button>Target</button>
</Tooltip>
</div>
))
.addStory('overflow-wrap', () => (
<div className={useStyles().wrapper}>
<Tooltip visible content="Thistooltiptextislongenoughtobebrokenintoanewline" relationship="description">
<button>Target</button>
</Tooltip>
</div>
))
.addStory('overflow-wrap withArrow', () => (
<div className={useStyles().wrapper}>
<Tooltip visible withArrow content="Thistooltiptextislongenoughtobebrokenintoanewline" relationship="description">
<button>Target</button>
</Tooltip>
</div>
));

const TooltipPositioning: React.FC = () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fix: infer a11y id from immediate header element",
"packageName": "@fluentui/react-card",
"email": "marcosvmmoura@gmail.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "bugfix: adds grid-template-columns to DialogBody styles to ensure grid template layout",
"packageName": "@fluentui/react-dialog",
"email": "bernardo.sunderhus@gmail.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "docs: improve types descriptions and fix TS circular references",
"packageName": "@fluentui/react-drawer",
"email": "marcosvmmoura@gmail.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fix: added overflowWrap to make sure content does not exceed the tooltip container and added vr test for the same",
"packageName": "@fluentui/react-tooltip",
"email": "kakrookaran@gmail.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ export type BreadcrumbSlots = {
// @public
export type BreadcrumbState = ComponentState<BreadcrumbSlots> & Required<Pick<BreadcrumbProps, 'appearance' | 'iconPosition' | 'size' | 'dividerType'>>;

// @public (undocumented)
export const isTruncatableBreadcrumbContent: (content: string, maxLength: number) => boolean;

// @public (undocumented)
export type PartitionBreadcrumbItems<T> = {
startDisplayedItems: readonly T[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ const useStyles = makeStyles({
':hover:active': {
...defaultButtonStyles,
},
':disabled': {
...defaultButtonStyles,
},
},
currentSmall: {
...typographyStyles.caption1Strong,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { BreadcrumbDividerProps } from './BreadcrumbDivider.types';
import type { ForwardRefComponent } from '@fluentui/react-utilities';

/**
* BreadcrumbDivider component - TODO: add more docs
* A divider component which is used inside the Breadcrumb
*/
export const BreadcrumbDivider: ForwardRefComponent<BreadcrumbDividerProps> = React.forwardRef((props, ref) => {
const state = useBreadcrumbDivider_unstable(props, ref);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import type { BreadcrumbItemProps } from './BreadcrumbItem.types';
import type { ForwardRefComponent } from '@fluentui/react-utilities';

/**
* BreadcrumbItem component - TODO: add more docs
* BreadcrumbItem component is a wrapper for BreadcrumbLink and BreadcrumbButton.
* It can be used as a non-interactive item.
*/
export const BreadcrumbItem: ForwardRefComponent<BreadcrumbItemProps> = React.forwardRef((props, ref) => {
const state = useBreadcrumbItem_unstable(props, ref);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { BreadcrumbLinkProps } from './BreadcrumbLink.types';
import type { ForwardRefComponent } from '@fluentui/react-utilities';

/**
* BreadcrumbLink component - TODO: add more docs
* A link component which is used inside the Breadcrumb.
*/
export const BreadcrumbLink: ForwardRefComponent<BreadcrumbLinkProps> = React.forwardRef((props, ref) => {
const state = useBreadcrumbLink_unstable(props, ref);
Expand Down
7 changes: 6 additions & 1 deletion packages/react-components/react-breadcrumb/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ export {
useBreadcrumbItem_unstable,
} from './BreadcrumbItem';
export type { BreadcrumbItemProps, BreadcrumbItemSlots, BreadcrumbItemState } from './BreadcrumbItem';
export { partitionBreadcrumbItems, truncateBreadcrumbLongName, truncateBreadcrumLongTooltip } from './utils/index';
export {
partitionBreadcrumbItems,
truncateBreadcrumbLongName,
truncateBreadcrumLongTooltip,
isTruncatableBreadcrumbContent,
} from './utils/index';
export type { PartitionBreadcrumbItemsOptions, PartitionBreadcrumbItems } from './utils/index';
export {
BreadcrumbButton,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
export { partitionBreadcrumbItems } from './partitionBreadcrumbItems';
export type { PartitionBreadcrumbItems, PartitionBreadcrumbItemsOptions } from './partitionBreadcrumbItems';
export { truncateBreadcrumbLongName, truncateBreadcrumLongTooltip } from './truncateBreadcrumb';
export {
truncateBreadcrumbLongName,
truncateBreadcrumLongTooltip,
isTruncatableBreadcrumbContent,
} from './truncateBreadcrumb';
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ const MAX_NAME_LENGTH = 30;
const MAX_TOOLTIP_LENGTH = 80;

const truncateBreadcrumb = (content: string, maxLength: number): string => {
return content.length > maxLength ? content.trim().slice(0, maxLength).concat('...') : content;
return isTruncatableBreadcrumbContent(content, maxLength)
? content.trim().slice(0, maxLength).concat('...')
: content;
};

export const isTruncatableBreadcrumbContent = (content: string, maxLength: number) => {
return content.length > maxLength;
};

export const truncateBreadcrumbLongName = (content: string, maxLength?: number): string => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@

- Don't use Breadcrumbs as a primary way to navigate an app or site.
- Avoid using custom dividers.
- Do not wrap breadcrumb items.

</details>
Original file line number Diff line number Diff line change
Expand Up @@ -277,3 +277,15 @@ export const BreadcrumbWithOverflow = () => {
</div>
);
};

BreadcrumbWithOverflow.parameters = {
docs: {
description: {
story: [
'The maximum number of items in a breadcrumb can be customized. We recommend a maximum of 6 items or fewer.',
'When the maximum number is exceeded, items in the middle auto-collapse into an overflow menu.',
'\nThe first and last items should always appear in the breadcrumb. Breadcrumbs should never wrap.',
].join('\n'),
},
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

### Do

- Use `slash` dividers only for small and non-interactive breadcrums.
- Use `slash` dividers only for small and non-interactive breadcrums. Use it to describe file paths.

### Don't

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
BreadcrumbButton,
truncateBreadcrumbLongName,
truncateBreadcrumLongTooltip,
isTruncatableBreadcrumbContent,
} from '@fluentui/react-breadcrumb';
import type { PartitionBreadcrumbItems } from '@fluentui/react-breadcrumb';
import { ArrowRight16Filled, MoreHorizontalRegular, MoreHorizontalFilled, bundleIcon } from '@fluentui/react-icons';
Expand Down Expand Up @@ -50,9 +51,14 @@ function renderItem(item: Item, size: BreadcrumbProps['size']) {
const isLastItem = items.length - 1 === item.key;
return (
<React.Fragment key={`${size}-item-${item.key}`}>
<Tooltip withArrow content={truncateBreadcrumLongTooltip(item.value)} relationship="label">
<BreadcrumbItem current={isLastItem}>{truncateBreadcrumbLongName(item.value)}</BreadcrumbItem>
</Tooltip>
{isTruncatableBreadcrumbContent(item.value, 30) ? (
<Tooltip withArrow content={truncateBreadcrumLongTooltip(item.value)} relationship="label">
<BreadcrumbItem current={isLastItem}>{truncateBreadcrumbLongName(item.value)}</BreadcrumbItem>
</Tooltip>
) : (
<BreadcrumbItem current={isLastItem}>{item.value}</BreadcrumbItem>
)}

{!isLastItem && <BreadcrumbDivider />}
</React.Fragment>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,29 @@ const CardSample = (props: CardProps) => (
</>
);

const CardWithCustomHeader = ({
customHeaderId = 'custom-header-id',
...props
}: CardProps & { customHeaderId: string }) => (
<>
<p tabIndex={0} id="before">
Before
</p>

<Card id="card" {...props}>
<CardHeader
image={<img src={resolveAsset('powerpoint_logo.svg')} alt="Microsoft PowerPoint logo" />}
header={<b id={customHeaderId}>App Name</b>}
description={<span>Developer</span>}
/>
</Card>

<p tabIndex={0} id="after">
After
</p>
</>
);

const CardWithPreview = (props: CardProps) => (
<>
<p tabIndex={0} id="before">
Expand Down Expand Up @@ -423,6 +446,17 @@ describe('Card', () => {
});
});

it('should sync selectable aria-labelledby with card header immediate child', () => {
const customHeaderId = 'custom-header';

mountFluent(<CardWithCustomHeader customHeaderId={customHeaderId} selected />);

cy.get(`.${cardHeaderClassNames.header}`).should('not.have.attr', 'id');
cy.get(`.${cardClassNames.checkbox}`).then(slot => {
cy.get(`#${customHeaderId}`).then(() => expect(customHeaderId).equals(slot.attr('aria-labelledby')));
});
});

it('should sync selectable aria-label with card preview alt', () => {
mountFluent(<CardWithPreview selected />);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,44 @@ import type { CardHeaderProps, CardHeaderState } from './CardHeader.types';
import { useCardContext_unstable } from '../Card/CardContext';
import { cardHeaderClassNames } from './useCardHeaderStyles.styles';

/**
* Finds the first child of CardHeader with an id prop.
*
* @param header - the header prop of CardHeader
*/
function getChildWithId(header: CardHeaderProps['header']) {
function isReactElementWithIdProp(element: React.ReactNode): element is React.ReactElement {
return React.isValidElement(element) && Boolean(element.props.id);
}

return React.Children.toArray(header).find(isReactElementWithIdProp);
}

/**
* Returns the id to use for the CardHeader root element.
*
* @param headerId - the id prop of the CardHeader component
* @param childWithId - the first child of the CardHeader component with an id prop
* @param generatedId - a generated id
*
* @returns the id to use for the CardHeader root element
*/
function getReferenceId(
headerId: string | undefined,
childWithId: React.ReactElement | undefined,
generatedId: string,
): string {
if (headerId) {
return headerId;
}

if (childWithId?.props.id) {
return childWithId.props.id;
}

return generatedId;
}

/**
* Create the state required to render CardHeader.
*
Expand All @@ -21,15 +59,17 @@ export const useCardHeader_unstable = (props: CardHeaderProps, ref: React.Ref<HT
} = useCardContext_unstable();
const headerRef = React.useRef<HTMLDivElement>(null);

const hasChildId = React.useRef(false);
const generatedId = useId(cardHeaderClassNames.header, referenceId);

React.useEffect(() => {
if (header && headerRef.current) {
const { id } = headerRef.current;
const headerId = !hasChildId.current ? headerRef.current?.id : undefined;
const childWithId = getChildWithId(header);

hasChildId.current = Boolean(childWithId);

setReferenceId(id ? id : generatedId);
}
}, [header, setReferenceId, generatedId]);
setReferenceId(getReferenceId(headerId, childWithId, generatedId));
}, [generatedId, header, setReferenceId]);

return {
components: {
Expand All @@ -49,7 +89,7 @@ export const useCardHeader_unstable = (props: CardHeaderProps, ref: React.Ref<HT
required: true,
defaultProps: {
ref: headerRef,
id: referenceId,
id: !hasChildId.current ? referenceId : undefined,
},
}),
description: resolveShorthand(description),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const useStyles = makeStyles({
...shorthands.overflow('unset'),
...shorthands.gap(DIALOG_GAP),
gridTemplateRows: 'auto 1fr',
gridTemplateColumns: '1fr 1fr auto',
[MEDIA_QUERY_BREAKPOINT_SELECTOR]: {
maxWidth: '100vw',
gridTemplateRows: 'auto 1fr auto',
Expand Down
12 changes: 12 additions & 0 deletions packages/react-components/react-drawer/etc/react-drawer.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,13 @@ export type DrawerInlineSlots = {
};

// @public
<<<<<<< HEAD
export type DrawerInlineState = ComponentState<DrawerInlineSlots> & DrawerInlineProps & DrawerBaseState;
||||||| 9b5b3f33ad
export type DrawerInlineState = ComponentState<DrawerInlineSlots> & DrawerBaseTypes & Pick<DrawerInlineProps, 'open' | 'separator'>;
=======
export type DrawerInlineState = ComponentState<DrawerInlineSlots> & DrawerInlineProps;
>>>>>>> master

// @public
export const DrawerOverlay: ForwardRefComponent<DrawerOverlayProps>;
Expand All @@ -143,7 +149,13 @@ export type DrawerOverlaySlots = DialogSurfaceSlots & {
};

// @public
<<<<<<< HEAD
export type DrawerOverlayState = ComponentState<DrawerOverlaySlots> & DrawerBaseProps & DrawerBaseState & {
||||||| 9b5b3f33ad
export type DrawerOverlayState = ComponentState<DrawerOverlaySlots> & DrawerBaseTypes & {
=======
export type DrawerOverlayState = ComponentState<DrawerOverlaySlots> & DrawerBaseProps & {
>>>>>>> master
dialog: DialogProps;
backdropVisible: boolean;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import * as React from 'react';
import { makeStyles, mergeClasses } from '@griffel/react';
import type { DrawerOverlaySlots, DrawerOverlayState } from './DrawerOverlay.types';
import { tokens } from '@fluentui/react-theme';
import type { SlotClassNames } from '@fluentui/react-utilities';

import type { DrawerOverlaySlots, DrawerOverlayState } from './DrawerOverlay.types';
import { getDrawerBaseClassNames, useDrawerBaseStyles } from '../../util/useDrawerBaseStyles.styles';
import { tokens } from '@fluentui/react-theme';
import * as React from 'react';

export const drawerOverlayClassNames: SlotClassNames<DrawerOverlaySlots> = {
root: 'fui-DrawerOverlay',
Expand Down Expand Up @@ -61,7 +62,7 @@ export const useDrawerOverlayStyles_unstable = (state: DrawerOverlayState): Draw
const rootStyles = useStyles();
const backdropStyles = useBackdropStyles();

const backdrop = state.root.backdrop as React.HTMLAttributes<HTMLDivElement>;
const backdrop = state.root.backdrop as React.HTMLAttributes<HTMLDivElement> | undefined;

state.root.className = mergeClasses(
drawerOverlayClassNames.root,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,14 @@ export const AlwaysOpen = () => {
</div>
);
};

AlwaysOpen.parameters = {
docs: {
description: {
story: [
'A drawer can be always open, in which case it will not be able to be closed by the user.',
'This is useful for drawers that are used for navigation, and should always be visible.',
].join('\n'),
},
},
};
Loading

0 comments on commit d2df033

Please sign in to comment.