Skip to content

Commit

Permalink
Merge branch 'master' into fix-44048-freeSolo-autocomplete-custom-pap…
Browse files Browse the repository at this point in the history
…er-popper
  • Loading branch information
ZeeshanTamboli committed Oct 30, 2024
2 parents d247bac + 2df4cec commit a6e38fe
Show file tree
Hide file tree
Showing 10 changed files with 403 additions and 0 deletions.
26 changes: 26 additions & 0 deletions docs/data/material/components/avatars/Spacing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as React from 'react';
import Avatar from '@mui/material/Avatar';
import AvatarGroup from '@mui/material/AvatarGroup';
import Stack from '@mui/material/Stack';

export default function Spacing() {
return (
<Stack spacing={4}>
<AvatarGroup spacing="medium">
<Avatar alt="Remy Sharp" src="/static/images/avatar/1.jpg" />
<Avatar alt="Travis Howard" src="/static/images/avatar/2.jpg" />
<Avatar alt="Cindy Baker" src="/static/images/avatar/3.jpg" />
</AvatarGroup>
<AvatarGroup spacing="small">
<Avatar alt="Remy Sharp" src="/static/images/avatar/1.jpg" />
<Avatar alt="Travis Howard" src="/static/images/avatar/2.jpg" />
<Avatar alt="Cindy Baker" src="/static/images/avatar/3.jpg" />
</AvatarGroup>
<AvatarGroup spacing={24}>
<Avatar alt="Remy Sharp" src="/static/images/avatar/1.jpg" />
<Avatar alt="Travis Howard" src="/static/images/avatar/2.jpg" />
<Avatar alt="Cindy Baker" src="/static/images/avatar/3.jpg" />
</AvatarGroup>
</Stack>
);
}
26 changes: 26 additions & 0 deletions docs/data/material/components/avatars/Spacing.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as React from 'react';
import Avatar from '@mui/material/Avatar';
import AvatarGroup from '@mui/material/AvatarGroup';
import Stack from '@mui/material/Stack';

export default function Spacing() {
return (
<Stack spacing={4}>
<AvatarGroup spacing="medium">
<Avatar alt="Remy Sharp" src="/static/images/avatar/1.jpg" />
<Avatar alt="Travis Howard" src="/static/images/avatar/2.jpg" />
<Avatar alt="Cindy Baker" src="/static/images/avatar/3.jpg" />
</AvatarGroup>
<AvatarGroup spacing="small">
<Avatar alt="Remy Sharp" src="/static/images/avatar/1.jpg" />
<Avatar alt="Travis Howard" src="/static/images/avatar/2.jpg" />
<Avatar alt="Cindy Baker" src="/static/images/avatar/3.jpg" />
</AvatarGroup>
<AvatarGroup spacing={24}>
<Avatar alt="Remy Sharp" src="/static/images/avatar/1.jpg" />
<Avatar alt="Travis Howard" src="/static/images/avatar/2.jpg" />
<Avatar alt="Cindy Baker" src="/static/images/avatar/3.jpg" />
</AvatarGroup>
</Stack>
);
}
15 changes: 15 additions & 0 deletions docs/data/material/components/avatars/Spacing.tsx.preview
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<AvatarGroup spacing="medium">
<Avatar alt="Remy Sharp" src="/static/images/avatar/1.jpg" />
<Avatar alt="Travis Howard" src="/static/images/avatar/2.jpg" />
<Avatar alt="Cindy Baker" src="/static/images/avatar/3.jpg" />
</AvatarGroup>
<AvatarGroup spacing="small">
<Avatar alt="Remy Sharp" src="/static/images/avatar/1.jpg" />
<Avatar alt="Travis Howard" src="/static/images/avatar/2.jpg" />
<Avatar alt="Cindy Baker" src="/static/images/avatar/3.jpg" />
</AvatarGroup>
<AvatarGroup spacing={24}>
<Avatar alt="Remy Sharp" src="/static/images/avatar/1.jpg" />
<Avatar alt="Travis Howard" src="/static/images/avatar/2.jpg" />
<Avatar alt="Cindy Baker" src="/static/images/avatar/3.jpg" />
</AvatarGroup>
6 changes: 6 additions & 0 deletions docs/data/material/components/avatars/avatars.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ The `renderSurplus` prop is useful when you need to render the surplus based on

{{"demo": "CustomSurplusAvatars.js"}}

### Spacing

You can change the spacing between avatars using the `spacing` prop. You can use one of the presets (`"medium"`, the default, or `"small"`) or set a custom numeric value.

{{"demo": "Spacing.js"}}

## With badge

{{"demo": "BadgeAvatars.js"}}
24 changes: 24 additions & 0 deletions packages/mui-utils/src/getReactNodeRef/getReactNodeRef.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as React from 'react';

/**
* Returns the ref of a React node handling differences between React 19 and older versions.
* It will return null if the node is not a valid React element.
*
* @param element React.ReactNode
* @returns React.Ref<any> | null
*
* @deprecated Use getReactElementRef instead
*/
export default function getReactNodeRef(element: React.ReactNode): React.Ref<any> | null {
if (!element || !React.isValidElement(element)) {
return null;
}

// 'ref' is passed as prop in React 19, whereas 'ref' is directly attached to children in older versions
return (element.props as any).propertyIsEnumerable('ref')
? (element.props as any).ref
: // @ts-expect-error element.ref is not included in the ReactElement type
// We cannot check for it, but isValidElement is true at this point
// https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/70189
element.ref;
}
1 change: 1 addition & 0 deletions packages/mui-utils/src/getReactNodeRef/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './getReactNodeRef';
2 changes: 2 additions & 0 deletions packages/mui-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export { default as unstable_useForkRef } from './useForkRef';
export { default as unstable_useLazyRef } from './useLazyRef';
export { default as unstable_useTimeout, Timeout as unstable_Timeout } from './useTimeout';
export { default as unstable_useOnMount } from './useOnMount';
export { default as unstable_useIsFocusVisible } from './useIsFocusVisible';
export { default as unstable_isFocusVisible } from './isFocusVisible';
export { default as unstable_getScrollbarSize } from './getScrollbarSize';
export { default as usePreviousProps } from './usePreviousProps';
Expand All @@ -45,5 +46,6 @@ export { default as unstable_useSlotProps } from './useSlotProps';
export type { UseSlotPropsParameters, UseSlotPropsResult } from './useSlotProps';
export { default as unstable_resolveComponentProps } from './resolveComponentProps';
export { default as unstable_extractEventHandlers } from './extractEventHandlers';
export { default as unstable_getReactNodeRef } from './getReactNodeRef';
export { default as unstable_getReactElementRef } from './getReactElementRef';
export * from './types';
2 changes: 2 additions & 0 deletions packages/mui-utils/src/useIsFocusVisible/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default } from './useIsFocusVisible';
export * from './useIsFocusVisible';
126 changes: 126 additions & 0 deletions packages/mui-utils/src/useIsFocusVisible/useIsFocusVisible.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { expect } from 'chai';
import * as React from 'react';
import * as ReactDOMClient from 'react-dom/client';
import {
act,
createRenderer,
focusVisible,
simulatePointerDevice,
programmaticFocusTriggersFocusVisible,
} from '@mui/internal-test-utils';
import useIsFocusVisible, { teardown as teardownFocusVisible } from './useIsFocusVisible';
import useForkRef from '../useForkRef';

const SimpleButton = React.forwardRef(function SimpleButton(props, ref) {
const {
isFocusVisibleRef,
onBlur: handleBlurVisible,
onFocus: handleFocusVisible,
ref: focusVisibleRef,
} = useIsFocusVisible();

const handleRef = useForkRef(focusVisibleRef, ref);

const [isFocusVisible, setIsFocusVisible] = React.useState(false);

const handleBlur = (event) => {
handleBlurVisible(event);
if (isFocusVisibleRef.current === false) {
setIsFocusVisible(false);
}
};

const handleFocus = (event) => {
handleFocusVisible(event);
if (isFocusVisibleRef.current === true) {
setIsFocusVisible(true);
}
};

return (
<button
type="button"
{...props}
ref={handleRef}
className={isFocusVisible ? 'focus-visible' : null}
onBlur={handleBlur}
onFocus={handleFocus}
/>
);
});

describe('useIsFocusVisible', () => {
const { render } = createRenderer();

before(() => {
// isolate test from previous component test that use the polyfill in the document scope
teardownFocusVisible(document);
});

describe('focus inside shadowRoot', () => {
before(function beforeHook() {
// Only run on HeadlessChrome which has native shadowRoot support.
// And jsdom which has limited support for shadowRoot (^12.0.0).
if (!/HeadlessChrome|jsdom/.test(window.navigator.userAgent)) {
this.skip();
}
});

let rootElement;
let reactRoot;

beforeEach(() => {
rootElement = document.createElement('div');
document.body.appendChild(rootElement);
rootElement.attachShadow({ mode: 'open' });
reactRoot = ReactDOMClient.createRoot(rootElement.shadowRoot);
});

afterEach(() => {
act(() => {
reactRoot.unmount();
});

teardownFocusVisible(rootElement.shadowRoot);
document.body.removeChild(rootElement);
});

it('should set focus state for shadowRoot children', () => {
const buttonRef = React.createRef();
render(
<SimpleButton id="test-button" ref={buttonRef}>
Hello
</SimpleButton>,
{},
{
container: rootElement.shadowRoot,
},
);
simulatePointerDevice();

const { current: button } = buttonRef;
if (button.nodeName !== 'BUTTON') {
throw new Error('missing button');
}

expect(button.classList.contains('focus-visible')).to.equal(false);

act(() => {
button.focus();
});

if (programmaticFocusTriggersFocusVisible()) {
expect(button).to.have.class('focus-visible');
} else {
expect(button).not.to.have.class('focus-visible');
}

act(() => {
button.blur();
});
focusVisible(button);

expect(button.classList.contains('focus-visible')).to.equal(true);
});
});
});
Loading

0 comments on commit a6e38fe

Please sign in to comment.