Update design of account search dropdown in collection editor (#38739)
This commit is contained in:
1 parent
ea33d7fba6
commit
05a1c170c2
8 files changed
+192
-66
No files matched your search
@@ -25,7 +25,7 @@
|
||||
.popover {
|
||||
z-index: 9999;
|
||||
box-sizing: border-box;
|
||||
max-height: max(200px, 30dvh);
|
||||
max-height: min(320px, 50dvh);
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
color: var(--color-text-primary);
|
||||
@@ -63,13 +63,7 @@
|
||||
user-select: none;
|
||||
|
||||
&[data-highlighted='true'] {
|
||||
color: var(--color-text-on-brand-base);
|
||||
background: var(--color-bg-brand-base);
|
||||
|
||||
&[aria-disabled='true'] {
|
||||
color: var(--color-text-on-disabled);
|
||||
background: var(--color-bg-disabled);
|
||||
}
|
||||
background: var(--color-bg-overlay-highlight);
|
||||
}
|
||||
|
||||
&[aria-disabled='true'] {
|
||||
|
||||
@@ -82,11 +82,12 @@ interface ComboboxProps<
|
||||
* Customise the rendering of group titles.
|
||||
* The `titleId` must be attached to the element that provides the
|
||||
* accessible name for the group.
|
||||
* Return `null` to omit rendering the group title.
|
||||
*/
|
||||
renderGroupTitle?: (
|
||||
groupKey: GroupKey,
|
||||
titleId: string,
|
||||
) => React.ReactElement | string;
|
||||
) => React.ReactElement | null;
|
||||
/**
|
||||
* The main selection handler, called when an option is selected or deselected.
|
||||
*/
|
||||
@@ -182,7 +183,12 @@ const ComboboxWithRef = <Item extends ComboboxItem, GroupKey extends string>(
|
||||
|
||||
const hasGroups = !Array.isArray(items);
|
||||
const flatItems = useMemo(
|
||||
() => (hasGroups ? (Object.values(items).flat() as Item[]) : items),
|
||||
() =>
|
||||
hasGroups
|
||||
? (Object.values(items)
|
||||
.flat()
|
||||
.filter((i) => !!i) as Item[])
|
||||
: items,
|
||||
[hasGroups, items],
|
||||
);
|
||||
|
||||
@@ -478,10 +484,11 @@ const ComboboxWithRef = <Item extends ComboboxItem, GroupKey extends string>(
|
||||
{(Object.keys(items) as GroupKey[]).map((groupKey) => {
|
||||
const groupItems = items[groupKey];
|
||||
const groupTitleId = `${listId}-group-${groupKey}`;
|
||||
const groupTitle = renderGroupTitle?.(
|
||||
const customGroupTitle = renderGroupTitle?.(
|
||||
groupKey,
|
||||
groupTitleId,
|
||||
) ?? <span id={groupTitleId}>{groupKey}</span>;
|
||||
);
|
||||
const hasTitle = customGroupTitle !== null;
|
||||
|
||||
if (!groupItems?.length) return null;
|
||||
|
||||
@@ -489,11 +496,18 @@ const ComboboxWithRef = <Item extends ComboboxItem, GroupKey extends string>(
|
||||
<ul
|
||||
key={groupKey}
|
||||
role='group'
|
||||
aria-labelledby={groupTitleId}
|
||||
aria-labelledby={hasTitle ? groupTitleId : undefined}
|
||||
>
|
||||
<li role='presentation' className={classes.groupLabel}>
|
||||
{groupTitle}
|
||||
</li>
|
||||
{hasTitle && (
|
||||
<li
|
||||
role='presentation'
|
||||
className={classes.groupLabel}
|
||||
>
|
||||
{customGroupTitle ?? (
|
||||
<span id={groupTitleId}>{groupKey}</span>
|
||||
)}
|
||||
</li>
|
||||
)}
|
||||
{renderItems(groupItems)}
|
||||
</ul>
|
||||
);
|
||||
|
||||
@@ -14,8 +14,9 @@ interface WrapperProps extends Omit<
|
||||
/**
|
||||
* A basic list item component that can be used as a base for more bespoke list items.
|
||||
*
|
||||
* Depending on functionality, use `ListItemButton` or `ListItemLink` as a child of the
|
||||
* wrapper component.
|
||||
* Choose the child of the wrapper component based on needed interactivity:
|
||||
* `ListItemContent` for a non-interactive item, `ListItemButton` or `ListItemLink`
|
||||
* for interactive items.
|
||||
*/
|
||||
export const ListItemWrapper: React.FC<WrapperProps> = ({
|
||||
icon,
|
||||
@@ -33,10 +34,31 @@ export const ListItemWrapper: React.FC<WrapperProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
interface LinkProps extends React.ComponentPropsWithoutRef<typeof Link> {
|
||||
interface WithSubtitle {
|
||||
subtitle?: React.ReactNode;
|
||||
}
|
||||
|
||||
interface ContentProps
|
||||
extends React.ComponentPropsWithoutRef<'h3'>, WithSubtitle {}
|
||||
|
||||
export const ListItemContent: React.FC<ContentProps> = ({
|
||||
subtitle,
|
||||
children,
|
||||
...otherProps
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<h3 className={classes.title} {...otherProps}>
|
||||
{children}
|
||||
</h3>
|
||||
{subtitle && <div className={classes.subtitle}>{subtitle}</div>}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
interface LinkProps
|
||||
extends React.ComponentPropsWithoutRef<typeof Link>, WithSubtitle {}
|
||||
|
||||
export const ListItemLink: React.FC<LinkProps> = ({
|
||||
subtitle,
|
||||
children,
|
||||
@@ -44,20 +66,16 @@ export const ListItemLink: React.FC<LinkProps> = ({
|
||||
...otherProps
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<h3 className={classes.title}>
|
||||
<Link className={classNames(className, 'focusable')} {...otherProps}>
|
||||
{children}
|
||||
</Link>
|
||||
</h3>
|
||||
{subtitle && <div className={classes.subtitle}>{subtitle}</div>}
|
||||
</>
|
||||
<ListItemContent subtitle={subtitle}>
|
||||
<Link className={classNames(className, 'focusable')} {...otherProps}>
|
||||
{children}
|
||||
</Link>
|
||||
</ListItemContent>
|
||||
);
|
||||
};
|
||||
|
||||
interface ButtonProps extends React.ComponentPropsWithoutRef<'button'> {
|
||||
subtitle?: React.ReactNode;
|
||||
}
|
||||
interface ButtonProps
|
||||
extends React.ComponentPropsWithoutRef<'button'>, WithSubtitle {}
|
||||
|
||||
export const ListItemButton: React.FC<ButtonProps> = ({
|
||||
subtitle,
|
||||
@@ -66,17 +84,14 @@ export const ListItemButton: React.FC<ButtonProps> = ({
|
||||
...otherProps
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<h3 className={classes.title}>
|
||||
<button
|
||||
type='button'
|
||||
className={classNames(className, 'focusable')}
|
||||
{...otherProps}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
</h3>
|
||||
{subtitle && <div className={classes.subtitle}>{subtitle}</div>}
|
||||
</>
|
||||
<ListItemContent subtitle={subtitle}>
|
||||
<button
|
||||
type='button'
|
||||
className={classNames(className, 'focusable')}
|
||||
{...otherProps}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
</ListItemContent>
|
||||
);
|
||||
};
|
||||
@@ -7,18 +7,31 @@ import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?reac
|
||||
|
||||
import { Icon } from '../icon';
|
||||
|
||||
import { ListItemWrapper, ListItemButton, ListItemLink } from './index';
|
||||
import {
|
||||
ListItemWrapper,
|
||||
ListItemContent,
|
||||
ListItemButton,
|
||||
ListItemLink,
|
||||
} from './index';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/ListItem',
|
||||
component: ListItemWrapper,
|
||||
subcomponents: { ListItemButton, ListItemLink },
|
||||
subcomponents: { ListItemContent, ListItemButton, ListItemLink },
|
||||
} satisfies Meta<typeof ListItemWrapper>;
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const NonInteractive: Story = {
|
||||
render: () => (
|
||||
<ListItemWrapper icon={<Icon icon={VisibilityIcon} id='visibility' />}>
|
||||
<ListItemContent>View more</ListItemContent>
|
||||
</ListItemWrapper>
|
||||
),
|
||||
};
|
||||
|
||||
export const WithButton: Story = {
|
||||
render: () => (
|
||||
<ListItemWrapper
|
||||
|
||||
Reference in new issue
Block a user