From 84affbac034346bc3ceab52f288e29ab4ec899b9 Mon Sep 17 00:00:00 2001 From: Pedro Bonamin Date: Mon, 30 Oct 2023 10:37:57 +0100 Subject: [PATCH 1/6] feat(facelift): create menu item StudioUI --- .eslintrc.cjs | 2 +- dev/test-studio/schema/book.ts | 12 ++ .../collapseMenu/CollapseOverflowMenu.tsx | 3 +- .../field/actions/FieldActionMenuItem.tsx | 6 +- .../object/BlockObjectActionsMenu.tsx | 12 +- .../PortableText/toolbar/BlockStyleSelect.tsx | 5 +- .../inputs/ReferenceInput/ReferenceField.tsx | 2 +- .../inputs/ReferenceInput/ReferenceItem.tsx | 2 +- .../ArrayOfObjectsFunctions.tsx | 13 +- .../ArrayOfObjectsInput/Grid/ErrorItem.tsx | 3 +- .../ArrayOfObjectsInput/Grid/GridItem.tsx | 14 +- .../arrays/ArrayOfObjectsInput/InsertMenu.tsx | 3 +- .../ArrayOfObjectsInput/List/ErrorItem.tsx | 5 +- .../ArrayOfObjectsInput/List/PreviewItem.tsx | 14 +- .../arrays/ArrayOfPrimitivesInput/ItemRow.tsx | 3 +- .../form/inputs/files/FileInput/FileInput.tsx | 12 +- .../inputs/files/ImageInput/ImageInput.tsx | 2 +- .../form/inputs/files/common/ActionsMenu.tsx | 4 +- .../FileInputMenuItem.styled.tsx | 2 +- .../FileInputMenuItem/FileInputMenuItem.tsx | 79 +++------ .../form/studio/assetSource/AssetMenu.tsx | 3 +- .../navbar/presence/PresenceMenu.tsx | 3 +- .../navbar/presence/PresenceMenuItem.tsx | 44 ++--- .../navbar/resources/ResourcesMenuItems.tsx | 14 +- .../navbar/search/components/SortMenu.tsx | 22 +-- .../filters/filter/OperatorsMenuButton.tsx | 26 ++- .../filters/filter/inputs/asset/Asset.tsx | 4 +- .../filter/inputs/string/StringList.tsx | 25 ++- .../components/navbar/userMenu/UserMenu.tsx | 2 +- .../navbar/workspace/WorkspaceMenuButton.tsx | 5 +- .../DocumentPane/DocumentActions.tsx | 12 +- .../PaneHeaderCreateButton.tsx | 3 +- .../document/statusBar/ActionMenuButton.tsx | 91 ++++------ packages/sanity/src/ui/index.ts | 1 + packages/sanity/src/ui/menuItem/MenuItem.tsx | 161 ++++++++++++++++++ packages/sanity/src/ui/menuItem/index.ts | 1 + 36 files changed, 315 insertions(+), 300 deletions(-) create mode 100644 packages/sanity/src/ui/menuItem/MenuItem.tsx create mode 100644 packages/sanity/src/ui/menuItem/index.ts diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 1bc86895255..1811bc9898d 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -221,7 +221,7 @@ module.exports = { paths: [ { name: '@sanity/ui', - importNames: ['Tooltip', 'TooltipProps'], + importNames: ['Tooltip', 'TooltipProps', 'MenuItem', 'MenuItemProps'], message: 'Please use the (more opinionated) exported components in sanity/src/ui instead.', }, diff --git a/dev/test-studio/schema/book.ts b/dev/test-studio/schema/book.ts index 738957b4c37..b9d29e24272 100644 --- a/dev/test-studio/schema/book.ts +++ b/dev/test-studio/schema/book.ts @@ -88,6 +88,18 @@ export default { }, ], }, + { + name: 'genre', + title: 'Genre', + type: 'string', + options: { + list: [ + {title: 'Fiction', value: 'fiction'}, + {title: 'Non Fiction', value: 'nonfiction'}, + {title: 'Poetry', value: 'poetry'}, + ], + }, + }, ], orderings: [ { diff --git a/packages/sanity/src/core/components/collapseMenu/CollapseOverflowMenu.tsx b/packages/sanity/src/core/components/collapseMenu/CollapseOverflowMenu.tsx index 3b37eb3cd02..01d4d1c0b49 100644 --- a/packages/sanity/src/core/components/collapseMenu/CollapseOverflowMenu.tsx +++ b/packages/sanity/src/core/components/collapseMenu/CollapseOverflowMenu.tsx @@ -1,5 +1,6 @@ -import {Menu, MenuButton, MenuButtonProps, MenuDivider, MenuItem} from '@sanity/ui' +import {Menu, MenuButton, MenuButtonProps, MenuDivider} from '@sanity/ui' import React from 'react' +import {MenuItem} from '../../../ui' import {CollapseMenuProps} from './CollapseMenu' const MENU_BUTTON_POPOVER_PROPS: MenuButtonProps['popover'] = { diff --git a/packages/sanity/src/core/form/field/actions/FieldActionMenuItem.tsx b/packages/sanity/src/core/form/field/actions/FieldActionMenuItem.tsx index 88cc5017289..497bd052de2 100644 --- a/packages/sanity/src/core/form/field/actions/FieldActionMenuItem.tsx +++ b/packages/sanity/src/core/form/field/actions/FieldActionMenuItem.tsx @@ -1,8 +1,9 @@ -import {MenuItem, Text} from '@sanity/ui' +import {Text} from '@sanity/ui' import React, {useCallback} from 'react' import {CheckmarkIcon} from '@sanity/icons' import {TooltipOfDisabled} from '../../../components' import {DocumentFieldActionItem} from '../../../config' +import {MenuItem} from '../../../../ui' export function FieldActionMenuItem(props: {action: DocumentFieldActionItem}) { const {action} = props @@ -19,13 +20,10 @@ export function FieldActionMenuItem(props: {action: DocumentFieldActionItem}) { diff --git a/packages/sanity/src/core/form/inputs/PortableText/object/BlockObjectActionsMenu.tsx b/packages/sanity/src/core/form/inputs/PortableText/object/BlockObjectActionsMenu.tsx index 39483104ad7..a34d03095a2 100644 --- a/packages/sanity/src/core/form/inputs/PortableText/object/BlockObjectActionsMenu.tsx +++ b/packages/sanity/src/core/form/inputs/PortableText/object/BlockObjectActionsMenu.tsx @@ -1,14 +1,5 @@ import {EditIcon, LinkIcon, TrashIcon, EyeOpenIcon, EllipsisVerticalIcon} from '@sanity/icons' -import { - Box, - Button, - Flex, - Menu, - MenuButton, - MenuButtonProps, - MenuItem, - useGlobalKeyDown, -} from '@sanity/ui' +import {Box, Button, Flex, Menu, MenuButton, MenuButtonProps, useGlobalKeyDown} from '@sanity/ui' import React, { forwardRef, ReactElement, @@ -20,6 +11,7 @@ import React, { PropsWithChildren, } from 'react' import {PortableTextBlock, isReference} from '@sanity/types' +import {MenuItem} from '../../../../../ui' import {IntentLink} from 'sanity/router' interface BlockObjectActionsMenuProps extends PropsWithChildren { diff --git a/packages/sanity/src/core/form/inputs/PortableText/toolbar/BlockStyleSelect.tsx b/packages/sanity/src/core/form/inputs/PortableText/toolbar/BlockStyleSelect.tsx index 0289fe7ac95..d04e56fe0ce 100644 --- a/packages/sanity/src/core/form/inputs/PortableText/toolbar/BlockStyleSelect.tsx +++ b/packages/sanity/src/core/form/inputs/PortableText/toolbar/BlockStyleSelect.tsx @@ -1,6 +1,6 @@ import React, {memo, useCallback, useMemo} from 'react' import {PortableTextEditor, usePortableTextEditor} from '@sanity/portable-text-editor' -import {Button, Menu, MenuButton, MenuButtonProps, MenuItem, Text} from '@sanity/ui' +import {Button, Menu, MenuButton, MenuButtonProps, Text} from '@sanity/ui' import {SelectIcon} from '@sanity/icons' import styled from 'styled-components' import { @@ -13,6 +13,7 @@ import { BlockQuote, Normal, } from '../text/textStyles' +import {MenuItem} from '../../../../../ui' import {useActiveStyleKeys, useFocusBlock} from './hooks' import {BlockStyleItem} from './types' @@ -145,6 +146,8 @@ export const BlockStyleSelect = memo(function BlockStyleSelect( // eslint-disable-next-line react/jsx-no-bind onClick={_disabled ? undefined : () => handleChange(item)} > + {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */} + {/* @ts-ignore: This item uses a specific render of the menu item.*/} {renderOption(item.style, item?.title || item.style)} ) diff --git a/packages/sanity/src/core/form/inputs/ReferenceInput/ReferenceField.tsx b/packages/sanity/src/core/form/inputs/ReferenceInput/ReferenceField.tsx index 2eeebc6f66b..be5d8b332b9 100644 --- a/packages/sanity/src/core/form/inputs/ReferenceInput/ReferenceField.tsx +++ b/packages/sanity/src/core/form/inputs/ReferenceInput/ReferenceField.tsx @@ -17,7 +17,6 @@ import { Menu, MenuButton, MenuDivider, - MenuItem, Stack, Text, } from '@sanity/ui' @@ -36,6 +35,7 @@ import {AlertStrip} from '../../components/AlertStrip' import {FieldActionsResolver} from '../../field' import {DocumentFieldActionNode} from '../../../config' import {useFormPublishedId} from '../../useFormPublishedId' +import {MenuItem} from '../../../../ui' import {useReferenceInput} from './useReferenceInput' import {useReferenceInfo} from './useReferenceInfo' import {PreviewReferenceValue} from './PreviewReferenceValue' diff --git a/packages/sanity/src/core/form/inputs/ReferenceInput/ReferenceItem.tsx b/packages/sanity/src/core/form/inputs/ReferenceInput/ReferenceItem.tsx index 9ab53889c0a..3069a4761f2 100644 --- a/packages/sanity/src/core/form/inputs/ReferenceInput/ReferenceItem.tsx +++ b/packages/sanity/src/core/form/inputs/ReferenceInput/ReferenceItem.tsx @@ -8,7 +8,6 @@ import { Menu, MenuButton, MenuDivider, - MenuItem, Spinner, Stack, Text, @@ -36,6 +35,7 @@ import {AlertStrip} from '../../components/AlertStrip' import {set, unset} from '../../patch' import {createProtoArrayValue} from '../arrays/ArrayOfObjectsInput/createProtoArrayValue' import {InsertMenu} from '../arrays/ArrayOfObjectsInput/InsertMenu' +import {MenuItem} from '../../../../ui' import {useReferenceInfo} from './useReferenceInfo' import {PreviewReferenceValue} from './PreviewReferenceValue' import {useReferenceInput} from './useReferenceInput' diff --git a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/ArrayOfObjectsFunctions.tsx b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/ArrayOfObjectsFunctions.tsx index 23e18210219..b25a8576690 100644 --- a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/ArrayOfObjectsFunctions.tsx +++ b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/ArrayOfObjectsFunctions.tsx @@ -1,18 +1,9 @@ import {ArraySchemaType, isReferenceSchemaType} from '@sanity/types' import {AddIcon} from '@sanity/icons' import React, {useId, useCallback} from 'react' -import { - Box, - Button, - Grid, - Menu, - MenuButton, - MenuItem, - Tooltip, - Text, - MenuButtonProps, -} from '@sanity/ui' +import {Box, Button, Grid, Menu, MenuButton, Tooltip, Text, MenuButtonProps} from '@sanity/ui' import {ArrayInputFunctionsProps, ObjectItem} from '../../../types' +import {MenuItem} from '../../../../../ui' const POPOVER_PROPS: MenuButtonProps['popover'] = { constrainSize: true, diff --git a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/Grid/ErrorItem.tsx b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/Grid/ErrorItem.tsx index cef935910e3..d1a7bb30fad 100644 --- a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/Grid/ErrorItem.tsx +++ b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/Grid/ErrorItem.tsx @@ -1,10 +1,11 @@ import React, {useCallback, useId} from 'react' import {EllipsisVerticalIcon, TrashIcon} from '@sanity/icons' -import {Button, Menu, MenuButton, MenuItem} from '@sanity/ui' +import {Button, Menu, MenuButton} from '@sanity/ui' import {ArrayItemError} from '../../../../store' import {useFormCallbacks} from '../../../../studio/contexts/FormCallbacks' import {PatchEvent, unset} from '../../../../patch' import {CellLayout} from '../../layouts/CellLayout' +import {MenuItem} from '../../../../../../ui' import {IncompatibleItemType} from './IncompatibleItemType' const MENU_POPOVER_PROPS = {portal: true, tone: 'default'} as const diff --git a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/Grid/GridItem.tsx b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/Grid/GridItem.tsx index f663756ff43..32a24dc9652 100644 --- a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/Grid/GridItem.tsx +++ b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/Grid/GridItem.tsx @@ -1,15 +1,4 @@ -import { - Box, - Button, - Card, - CardTone, - Flex, - Menu, - MenuButton, - MenuItem, - Spinner, - Text, -} from '@sanity/ui' +import {Box, Button, Card, CardTone, Flex, Menu, MenuButton, Spinner, Text} from '@sanity/ui' import React, {useCallback, useMemo, useRef} from 'react' import {SchemaType} from '@sanity/types' import {CopyIcon as DuplicateIcon, EllipsisVerticalIcon, TrashIcon} from '@sanity/icons' @@ -29,6 +18,7 @@ import {createProtoArrayValue} from '../createProtoArrayValue' import {InsertMenu} from '../InsertMenu' import {FIXME} from '../../../../../FIXME' import {EditPortal} from '../../../../components/EditPortal' +import {MenuItem} from '../../../../../../ui' type GridItemProps = Omit, 'renderDefault'> diff --git a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/InsertMenu.tsx b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/InsertMenu.tsx index 507511b649d..b2b62830570 100644 --- a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/InsertMenu.tsx +++ b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/InsertMenu.tsx @@ -1,8 +1,9 @@ /* eslint-disable react/jsx-no-bind */ import {SchemaType} from '@sanity/types' import React, {ComponentProps, memo} from 'react' -import {MenuGroup, MenuItem, PopoverProps} from '@sanity/ui' +import {MenuGroup, PopoverProps} from '@sanity/ui' import {InsertAboveIcon, InsertBelowIcon} from '@sanity/icons' +import {MenuItem} from '../../../../../ui' interface Props { types?: SchemaType[] diff --git a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/List/ErrorItem.tsx b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/List/ErrorItem.tsx index d3a72602c6d..7b895e9c706 100644 --- a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/List/ErrorItem.tsx +++ b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/List/ErrorItem.tsx @@ -1,6 +1,7 @@ -import React, {useCallback, useId} from 'react' +import React, {useId} from 'react' import {EllipsisVerticalIcon, TrashIcon} from '@sanity/icons' -import {Box, Button, Menu, MenuButton, MenuItem} from '@sanity/ui' +import {Box, Button, Menu, MenuButton} from '@sanity/ui' +import {MenuItem} from '../../../../../../ui' import {ArrayItemError} from '../../../../store' import {RowLayout} from '../../layouts/RowLayout' import {IncompatibleItemType} from './IncompatibleItemType' diff --git a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/List/PreviewItem.tsx b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/List/PreviewItem.tsx index 64e9c890564..0d4ac3b93ae 100644 --- a/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/List/PreviewItem.tsx +++ b/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/List/PreviewItem.tsx @@ -1,15 +1,4 @@ -import { - Box, - Button, - Card, - CardTone, - Flex, - Menu, - MenuButton, - MenuItem, - Spinner, - Text, -} from '@sanity/ui' +import {Box, Button, Card, CardTone, Flex, Menu, MenuButton, Spinner, Text} from '@sanity/ui' import React, {useCallback, useMemo, useRef} from 'react' import {SchemaType} from '@sanity/types' import {CopyIcon as DuplicateIcon, EllipsisVerticalIcon, TrashIcon} from '@sanity/icons' @@ -27,6 +16,7 @@ import {RowLayout} from '../../layouts/RowLayout' import {createProtoArrayValue} from '../createProtoArrayValue' import {InsertMenu} from '../InsertMenu' import {EditPortal} from '../../../../components/EditPortal' +import {MenuItem} from '../../../../../../ui' type PreviewItemProps = Omit, 'renderDefault'> diff --git a/packages/sanity/src/core/form/inputs/arrays/ArrayOfPrimitivesInput/ItemRow.tsx b/packages/sanity/src/core/form/inputs/arrays/ArrayOfPrimitivesInput/ItemRow.tsx index ec19d867195..e28e7272031 100644 --- a/packages/sanity/src/core/form/inputs/arrays/ArrayOfPrimitivesInput/ItemRow.tsx +++ b/packages/sanity/src/core/form/inputs/arrays/ArrayOfPrimitivesInput/ItemRow.tsx @@ -1,5 +1,5 @@ import React, {useCallback, useMemo} from 'react' -import {Box, Button, Flex, Menu, MenuButton, MenuItem} from '@sanity/ui' +import {Box, Button, Flex, Menu, MenuButton} from '@sanity/ui' import {SchemaType} from '@sanity/types' import {CopyIcon as DuplicateIcon, EllipsisVerticalIcon, TrashIcon} from '@sanity/icons' import {FormFieldValidationStatus} from '../../../components/formField' @@ -7,6 +7,7 @@ import {InsertMenu} from '../ArrayOfObjectsInput/InsertMenu' import {PrimitiveItemProps} from '../../../types/itemProps' import {RowLayout} from '../layouts/RowLayout' import {FieldPresence} from '../../../../presence' +import {MenuItem} from '../../../../../ui' import {getEmptyValue} from './getEmptyValue' export type DefaultItemProps = Omit & { diff --git a/packages/sanity/src/core/form/inputs/files/FileInput/FileInput.tsx b/packages/sanity/src/core/form/inputs/files/FileInput/FileInput.tsx index d1f463ca7ac..6574ccfc77e 100644 --- a/packages/sanity/src/core/form/inputs/files/FileInput/FileInput.tsx +++ b/packages/sanity/src/core/form/inputs/files/FileInput/FileInput.tsx @@ -13,16 +13,7 @@ import { UploadState, } from '@sanity/types' import {ImageIcon, SearchIcon} from '@sanity/icons' -import { - Box, - Button, - Card, - Menu, - MenuButton, - MenuItem, - ThemeColorToneKey, - ToastParams, -} from '@sanity/ui' +import {Box, Button, Card, Menu, MenuButton, ThemeColorToneKey, ToastParams} from '@sanity/ui' import {SanityClient} from '@sanity/client' import {isFileSource} from '@sanity/asset-utils' import {WithReferencedAsset} from '../../../utils/WithReferencedAsset' @@ -38,6 +29,7 @@ import {InputProps, ObjectInputProps} from '../../../types' import {PatchEvent, setIfMissing, unset} from '../../../patch' import {MemberField, MemberFieldError, MemberFieldSet} from '../../../members' import {ImperativeToast} from '../../../../components' +import {MenuItem} from '../../../../../ui' import {ChangeIndicator} from '../../../../changeIndicators' import {CardOverlay, FlexContainer} from './styles' import {FileActionsMenu} from './FileActionsMenu' diff --git a/packages/sanity/src/core/form/inputs/files/ImageInput/ImageInput.tsx b/packages/sanity/src/core/form/inputs/files/ImageInput/ImageInput.tsx index 41de27bfa8e..4ec20508227 100644 --- a/packages/sanity/src/core/form/inputs/files/ImageInput/ImageInput.tsx +++ b/packages/sanity/src/core/form/inputs/files/ImageInput/ImageInput.tsx @@ -9,7 +9,6 @@ import { Menu, MenuButton, MenuButtonProps, - MenuItem, Stack, ToastParams, } from '@sanity/ui' @@ -52,6 +51,7 @@ import {PresenceOverlay} from '../../../../presence' import {FIXME} from '../../../../FIXME' import {ImperativeToast} from '../../../../components' import {ChangeIndicator} from '../../../../changeIndicators' +import {MenuItem} from '../../../../../ui' import {ImageActionsMenu} from './ImageActionsMenu' import {ImagePreview} from './ImagePreview' import {InvalidImageWarning} from './InvalidImageWarning' diff --git a/packages/sanity/src/core/form/inputs/files/common/ActionsMenu.tsx b/packages/sanity/src/core/form/inputs/files/common/ActionsMenu.tsx index d254d1bd653..2c0502a4574 100644 --- a/packages/sanity/src/core/form/inputs/files/common/ActionsMenu.tsx +++ b/packages/sanity/src/core/form/inputs/files/common/ActionsMenu.tsx @@ -1,7 +1,8 @@ import React, {MouseEventHandler, useCallback} from 'react' import {UploadIcon, CopyIcon, ResetIcon, DownloadIcon} from '@sanity/icons' -import {Box, MenuItem, MenuDivider, Label, useToast} from '@sanity/ui' +import {Box, MenuDivider, Label, useToast} from '@sanity/ui' +import {MenuItem} from '../../../../../ui' import {FileInputMenuItem} from './FileInputMenuItem/FileInputMenuItem' interface Props { @@ -40,7 +41,6 @@ export function ActionsMenu(props: Props) { text="Upload" data-testid="file-input-upload-button" disabled={readOnly || !directUploads} - fontSize={2} /> {browse} diff --git a/packages/sanity/src/core/form/inputs/files/common/FileInputMenuItem/FileInputMenuItem.styled.tsx b/packages/sanity/src/core/form/inputs/files/common/FileInputMenuItem/FileInputMenuItem.styled.tsx index a6b29dd6acd..45a574b13b2 100644 --- a/packages/sanity/src/core/form/inputs/files/common/FileInputMenuItem/FileInputMenuItem.styled.tsx +++ b/packages/sanity/src/core/form/inputs/files/common/FileInputMenuItem/FileInputMenuItem.styled.tsx @@ -1,5 +1,5 @@ -import {MenuItem} from '@sanity/ui' import styled from 'styled-components' +import {MenuItem} from '../../../../../../ui' export const FileMenuItem = styled(MenuItem)` position: relative; diff --git a/packages/sanity/src/core/form/inputs/files/common/FileInputMenuItem/FileInputMenuItem.tsx b/packages/sanity/src/core/form/inputs/files/common/FileInputMenuItem/FileInputMenuItem.tsx index de864737015..3f1c12b4a21 100644 --- a/packages/sanity/src/core/form/inputs/files/common/FileInputMenuItem/FileInputMenuItem.tsx +++ b/packages/sanity/src/core/form/inputs/files/common/FileInputMenuItem/FileInputMenuItem.tsx @@ -1,4 +1,4 @@ -import React, {createElement, isValidElement, useId} from 'react' +import React, {createElement, isValidElement, useCallback, useId} from 'react' import {isValidElementType} from 'react-is' import {Box, ButtonProps, Flex, Text} from '@sanity/ui' import {FileMenuItem} from './FileInputMenuItem.styled' @@ -16,21 +16,7 @@ export const FileInputMenuItem = React.forwardRef(function FileInputMenuItem( Omit, 'as' | 'ref' | 'type' | 'value' | 'onSelect'>, forwardedRef: React.ForwardedRef, ) { - const { - icon, - id: idProp, - accept, - capture, - fontSize, - multiple, - onSelect, - padding = 3, - space = 3, - textAlign, - text, - disabled, - ...rest - } = props + const {icon, id: idProp, accept, capture, multiple, onSelect, text, disabled, ...rest} = props const id = `${idProp || ''}-${useId()}` const handleChange = React.useCallback( @@ -42,50 +28,35 @@ export const FileInputMenuItem = React.forwardRef(function FileInputMenuItem( [onSelect], ) - const content = ( - - {/* Icon */} - {icon && ( - - - {isValidElement(icon) && icon} - {isValidElementType(icon) && createElement(icon)} - - - )} - - {/* Text */} - {text && ( - - {text} - - )} - + const renderMenuItem = useCallback( + (item: React.JSX.Element) => ( +
+ {item} + {/* Visibly hidden input */} + +
+ ), + [accept, capture, disabled, handleChange, id, multiple], ) - return ( - {content} - - {/* Visibly hidden input */} - - + icon={icon} + text={text} + renderMenuItem={renderMenuItem} + /> ) }) diff --git a/packages/sanity/src/core/form/studio/assetSource/AssetMenu.tsx b/packages/sanity/src/core/form/studio/assetSource/AssetMenu.tsx index 8a21b03382c..fb379fd9c80 100644 --- a/packages/sanity/src/core/form/studio/assetSource/AssetMenu.tsx +++ b/packages/sanity/src/core/form/studio/assetSource/AssetMenu.tsx @@ -1,6 +1,7 @@ import React from 'react' import {LinkIcon, EllipsisVerticalIcon, TrashIcon} from '@sanity/icons' -import {Button, Menu, MenuItem, MenuButton} from '@sanity/ui' +import {Button, Menu, MenuButton} from '@sanity/ui' +import {MenuItem} from '../../../../ui' import {AssetMenuAction} from './types' export function AssetMenu({ diff --git a/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenu.tsx b/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenu.tsx index d5eb2eb940d..6ca5e19d1cb 100644 --- a/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenu.tsx +++ b/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenu.tsx @@ -7,7 +7,6 @@ import { Menu, MenuButton, MenuDivider, - MenuItem, Stack, Text, } from '@sanity/ui' @@ -16,6 +15,7 @@ import styled from 'styled-components' import {StatusButton, UserAvatar} from '../../../../components' import {useGlobalPresence} from '../../../../store' import {useColorScheme} from '../../../colorScheme' +import {MenuItem} from '../../../../../ui' import {useWorkspace} from '../../../workspace' import {PresenceMenuItem} from './PresenceMenuItem' @@ -131,7 +131,6 @@ export function PresenceMenu(props: PresenceMenuProps) { href={`https://sanity.io/manage/project/${projectId}`} iconRight={CogIcon} onFocus={handleClearFocusedItem} - paddingY={4} rel="noopener noreferrer" target="_blank" text="Manage members" diff --git a/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenuItem.tsx b/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenuItem.tsx index 581adbec073..375e435bb41 100644 --- a/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenuItem.tsx +++ b/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenuItem.tsx @@ -1,17 +1,11 @@ import React, {forwardRef, memo, useCallback, useEffect, useMemo, useState} from 'react' import {orderBy} from 'lodash' import * as PathUtils from '@sanity/util/paths' -import {Card, Flex, MenuItem, Stack, Text} from '@sanity/ui' -import styled from 'styled-components' import {GlobalPresence} from '../../../../store' import {UserAvatar} from '../../../../components' +import {MenuItem} from '../../../../../ui' import {IntentLink} from 'sanity/router' -const AvatarCard = styled(Card)` - background: transparent; - margin: calc((-35px + 11px) / 2); -` - interface PresenceListRowProps { focused: boolean onFocus: (id: string) => void @@ -69,33 +63,21 @@ export const PresenceMenuItem = memo(function PresenceMenuItem(props: PresenceLi return ( - - - - - - - {presence.user.displayName} - {!hasLink && ( - - Not in a document - - )} - - - + text={presence.user.displayName} + subText={hasLink ? undefined : 'Not in a document'} + avatar={ + + } + /> ) }) diff --git a/packages/sanity/src/core/studio/components/navbar/resources/ResourcesMenuItems.tsx b/packages/sanity/src/core/studio/components/navbar/resources/ResourcesMenuItems.tsx index a106b0fec1e..6166d1f2700 100644 --- a/packages/sanity/src/core/studio/components/navbar/resources/ResourcesMenuItems.tsx +++ b/packages/sanity/src/core/studio/components/navbar/resources/ResourcesMenuItems.tsx @@ -1,6 +1,7 @@ -import {Box, Card, Flex, Label, MenuDivider, MenuItem, Spinner, Text} from '@sanity/ui' +import {Box, Card, Flex, Label, MenuDivider, Spinner, Text} from '@sanity/ui' import React from 'react' import {SANITY_VERSION} from '../../../../version' +import {MenuItem} from '../../../../../ui' import {ResourcesResponse, Section} from './helper-functions/types' interface ResourcesMenuItemProps { @@ -54,26 +55,20 @@ const fallbackLinks = ( @@ -100,7 +95,6 @@ function SubSection({subSection}: {subSection: Section}) { tone="default" key={item._key} text={item.title} - size={0} href={item.url} target="_blank" /> @@ -108,9 +102,7 @@ function SubSection({subSection}: {subSection: Section}) { case 'internalAction': // TODO: Add support for internal actions (MVI-2) if (!item.type) return null return ( - item.type === 'show-welcome-modal' && ( - - ) + item.type === 'show-welcome-modal' && ) default: diff --git a/packages/sanity/src/core/studio/components/navbar/search/components/SortMenu.tsx b/packages/sanity/src/core/studio/components/navbar/search/components/SortMenu.tsx index 0b8b844ce58..57e16f75650 100644 --- a/packages/sanity/src/core/studio/components/navbar/search/components/SortMenu.tsx +++ b/packages/sanity/src/core/studio/components/navbar/search/components/SortMenu.tsx @@ -1,22 +1,12 @@ import {SortIcon} from '@sanity/icons' -import { - Box, - Button, - Card, - Flex, - Inline, - Menu, - MenuButton, - MenuDivider, - MenuItem, - Text, -} from '@sanity/ui' +import {Box, Button, Card, Flex, Inline, Menu, MenuButton, MenuDivider, Text} from '@sanity/ui' import isEqual from 'lodash/isEqual' import React, {useCallback, useId, useMemo} from 'react' import styled from 'styled-components' import {ORDERINGS} from '../definitions/orderings' import {useSearchState} from '../contexts/search/useSearchState' import type {SearchOrdering} from '../types' +import {MenuItem} from '../../../../../../ui' interface SearchDivider { type: 'divider' @@ -53,13 +43,7 @@ function CustomMenuItem({ordering}: {ordering: SearchOrdering}) { const isSelected = useMemo(() => isEqual(currentOrdering, ordering), [currentOrdering, ordering]) return ( - - - - {ordering.title} - - - + ) } diff --git a/packages/sanity/src/core/studio/components/navbar/search/components/filters/filter/OperatorsMenuButton.tsx b/packages/sanity/src/core/studio/components/navbar/search/components/filters/filter/OperatorsMenuButton.tsx index 588fd80c362..2b752aa5c87 100644 --- a/packages/sanity/src/core/studio/components/navbar/search/components/filters/filter/OperatorsMenuButton.tsx +++ b/packages/sanity/src/core/studio/components/navbar/search/components/filters/filter/OperatorsMenuButton.tsx @@ -1,11 +1,12 @@ import {SelectIcon} from '@sanity/icons' -import {Box, Button, Flex, Inline, Menu, MenuButton, MenuDivider, MenuItem, Text} from '@sanity/ui' -import React, {createElement, useCallback, useId} from 'react' +import {Box, Button, Flex, Inline, Menu, MenuButton, MenuDivider, Text} from '@sanity/ui' +import React, {useCallback, useId} from 'react' import {useSearchState} from '../../../contexts/search/useSearchState' import {getFilterDefinition} from '../../../definitions/filters' import {getOperatorDefinition, SearchOperatorDefinition} from '../../../definitions/operators' import type {SearchFilter} from '../../../types' import {getFilterKey} from '../../../utils/filterUtils' +import {MenuItem} from '../../../../../../../../ui' interface OperatorsMenuButtonProps { filter: SearchFilter @@ -24,20 +25,13 @@ function CustomMenuItem({ const handleClick = useCallback(() => onClick(operator.type), [onClick, operator.type]) return ( - - - - - {operator.label} - - - {operator?.icon && ( - - {createElement(operator.icon)} - - )} - - + ) } diff --git a/packages/sanity/src/core/studio/components/navbar/search/components/filters/filter/inputs/asset/Asset.tsx b/packages/sanity/src/core/studio/components/navbar/search/components/filters/filter/inputs/asset/Asset.tsx index 914953d0347..2ecff253bc3 100644 --- a/packages/sanity/src/core/studio/components/navbar/search/components/filters/filter/inputs/asset/Asset.tsx +++ b/packages/sanity/src/core/studio/components/navbar/search/components/filters/filter/inputs/asset/Asset.tsx @@ -1,6 +1,6 @@ import {ChevronDownIcon, ImageIcon, SearchIcon, UndoIcon} from '@sanity/icons' import type {AssetFromSource, AssetSource, ReferenceValue} from '@sanity/types' -import {Box, Button, Flex, Menu, MenuButton, MenuItem, Portal, Stack} from '@sanity/ui' +import {Box, Button, Flex, Menu, MenuButton, Portal, Stack} from '@sanity/ui' import {get} from 'lodash' import React, {useCallback, useEffect, useId, useMemo, useState} from 'react' import styled from 'styled-components' @@ -11,6 +11,7 @@ import {DEFAULT_STUDIO_CLIENT_OPTIONS} from '../../../../../../../../../studioCl import {useSource} from '../../../../../../../../source' import {useSearchState} from '../../../../../contexts/search/useSearchState' import type {OperatorInputComponentProps} from '../../../../../definitions/operators/operatorTypes' +import {MenuItem} from '../../../../../../../../../../ui' import {AssetSourceError} from './AssetSourceError' import {AssetPreview} from './preview/AssetPreview' @@ -149,7 +150,6 @@ export function SearchFilterAssetInput(type?: AssetType) { {assetSources.map((source) => ( , 'title'> { title: (number | string)[] @@ -27,20 +28,14 @@ function CustomMenuItem({ const handleClick = useCallback(() => onClick(value), [onClick, value]) return ( - - - - - {title} - - {value && ( - - {value} - - )} - - - + ) } diff --git a/packages/sanity/src/core/studio/components/navbar/userMenu/UserMenu.tsx b/packages/sanity/src/core/studio/components/navbar/userMenu/UserMenu.tsx index ad170be1d69..d8ce3d43f48 100644 --- a/packages/sanity/src/core/studio/components/navbar/userMenu/UserMenu.tsx +++ b/packages/sanity/src/core/studio/components/navbar/userMenu/UserMenu.tsx @@ -9,7 +9,6 @@ import { MenuButton, MenuButtonProps, MenuDivider, - MenuItem, Stack, Text, Tooltip, @@ -26,6 +25,7 @@ import { } from '../../../colorScheme' import {useWorkspace} from '../../../workspace' import {userHasRole} from '../../../../util/userHasRole' +import {MenuItem} from '../../../../../ui' import {LoginProviderLogo} from './LoginProviderLogo' const AVATAR_SIZE = 1 diff --git a/packages/sanity/src/core/studio/components/navbar/workspace/WorkspaceMenuButton.tsx b/packages/sanity/src/core/studio/components/navbar/workspace/WorkspaceMenuButton.tsx index 67fc4f3d7dd..9c5dedabdbb 100644 --- a/packages/sanity/src/core/studio/components/navbar/workspace/WorkspaceMenuButton.tsx +++ b/packages/sanity/src/core/studio/components/navbar/workspace/WorkspaceMenuButton.tsx @@ -2,7 +2,6 @@ import {SelectIcon} from '@sanity/icons' import { Button, MenuButton, - MenuItem, Menu, MenuButtonProps, Box, @@ -16,6 +15,7 @@ import styled from 'styled-components' import {useActiveWorkspace} from '../../../activeWorkspaceMatcher' import {useColorScheme} from '../../../colorScheme' import {useWorkspaces} from '../../../workspaces' +import {MenuItem} from '../../../../../ui' import {useWorkspaceAuthStates} from './hooks' import {WorkspacePreview} from './WorkspacePreview' import {useRouter} from 'sanity/router' @@ -112,9 +112,10 @@ export function WorkspaceMenuButton(props: WorkspaceMenuButtonProps) { key={workspace.name} // eslint-disable-next-line react/jsx-no-bind onClick={handleSelectWorkspace} - padding={2} pressed={workspace.name === activeWorkspace.name} > + {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */} + {/* @ts-ignore - Workspace preview hasn't been redesigned for facelift */} - {actionState.title} - + const menuItemContent = useCallback( + (item: React.JSX.Element) => { + // TODO: Once the tooltip changes land, we can use the new `content` prop instead + const tooltipContent = actionState.title && ( + + {actionState.title} + + ) + return ( + + {item} + + ) + }, + [actionState.title], ) - return ( - - - - {actionState.icon && ( - - - {isValidElement(actionState.icon) && actionState.icon} - {isValidElementType(actionState.icon) && createElement(actionState.icon)} - - - )} - - {actionState.label} - - - {actionState.shortcut && ( - - s.slice(0, 1).toUpperCase() + s.slice(1))} - /> - - )} - - - + icon={actionState.icon} + text={actionState.label} + hotkeys={ + actionState.shortcut + ? String(actionState.shortcut) + .split('+') + .map((s) => s.slice(0, 1).toUpperCase() + s.slice(1)) + : undefined + } + renderMenuItem={menuItemContent} + /> ) } diff --git a/packages/sanity/src/ui/index.ts b/packages/sanity/src/ui/index.ts index fa5a7e52455..d1786efffbb 100644 --- a/packages/sanity/src/ui/index.ts +++ b/packages/sanity/src/ui/index.ts @@ -1 +1,2 @@ export * from './tooltip' +export * from './menuItem' diff --git a/packages/sanity/src/ui/menuItem/MenuItem.tsx b/packages/sanity/src/ui/menuItem/MenuItem.tsx new file mode 100644 index 00000000000..f8d9e376698 --- /dev/null +++ b/packages/sanity/src/ui/menuItem/MenuItem.tsx @@ -0,0 +1,161 @@ +import { + Flex, + MenuItem as UIMenuItem, + MenuItemProps as UIMenuItemProps, + Box, + Text, + Badge, + Stack, + Hotkeys, +} from '@sanity/ui' +import React, {createElement, forwardRef, isValidElement, useMemo} from 'react' +import {isValidElementType} from 'react-is' + +interface LargeMenuItem { + size: 'large' + subText?: string + badgeText?: string + avatar?: React.ReactNode + /** + * Hotkeys are only supported in `size="small"` menu items. + */ + hotkeys?: undefined + /** + * Icon is only supported in `size="small"` menu items. + */ + icon?: undefined +} + +interface SmallMenuItem { + size?: 'small' + hotkeys?: UIMenuItemProps['hotkeys'] + /** + * Sub text is only supported in `size="large"` menu items. + */ + subText?: undefined + /** + * Badge text is only supported in `size="large"` menu items. + */ + badgeText?: undefined + /** + * Avatar is only supported in `size="large"` menu items. + */ + avatar?: undefined +} + +const fontSize = 1 + +/** @internal */ +export type MenuItemProps = Pick< + UIMenuItemProps, + 'as' | 'icon' | 'iconRight' | 'pressed' | 'selected' | 'text' | 'tone' +> & + (LargeMenuItem | SmallMenuItem) & + Omit< + React.HTMLProps, + 'as' | 'height' | 'ref' | 'selected' | 'tabIndex' | 'size' + > & { + /** + * Allows to add wrappers to the menu item, e.g. `Tooltip`. + */ + renderMenuItem?: (menuItem: React.JSX.Element) => React.ReactNode + /** + * Usage of `children` is not recommended but still supported. + * Try using `renderMenuItem` instead. + * To use children opt out with `@ts-ignore`. + */ + children?: undefined + } + +/** + * Studio UI . + * + * Studio UI components are opinionated `@sanity/ui` components meant for internal use only. + * Props and options are intentionally limited to ensure consistency and ease of use. + * + * @internal + */ +export const MenuItem = forwardRef(function MenuItem( + { + size = 'small', + badgeText, + subText, + text, + avatar = null, + icon, + iconRight, + hotkeys, + children, + ...props + }: MenuItemProps, + ref: React.Ref, +) { + const menuItemContent = useMemo(() => { + if (size === 'large') { + return ( + + {avatar} + {(text || subText) && ( + + {text && ( + + {text} + + )} + {subText && ( + + {subText} + + )} + + )} + {badgeText && ( + + {badgeText} + + )} + + ) + } + + return ( + + {icon && ( + + {isValidElement(icon) && icon} + {isValidElementType(icon) && createElement(icon)} + + )} + + {text && ( + + + {text} + + + )} + + {hotkeys && ( + + )} + + {iconRight && ( + + {isValidElement(iconRight) && iconRight} + {isValidElementType(iconRight) && createElement(iconRight)} + + )} + + ) + }, [size, icon, text, hotkeys, iconRight, avatar, subText, badgeText]) + + return ( + + {/* Not recommended, should opt out with ts-ignore to use it. */} + {typeof children !== 'undefined' && children} + {typeof children === 'undefined' && typeof props.renderMenuItem === 'function' + ? props.renderMenuItem(menuItemContent) + : menuItemContent} + + ) +}) diff --git a/packages/sanity/src/ui/menuItem/index.ts b/packages/sanity/src/ui/menuItem/index.ts new file mode 100644 index 00000000000..7f0d25e4824 --- /dev/null +++ b/packages/sanity/src/ui/menuItem/index.ts @@ -0,0 +1 @@ +export * from './MenuItem' From a119ef0c097d75cafadd20177e6ea6e351e7816e Mon Sep 17 00:00:00 2001 From: Pedro Bonamin Date: Thu, 2 Nov 2023 09:15:19 +0100 Subject: [PATCH 2/6] fix(facelift): refactor workspace preview to use large menu item --- .../navbar/presence/PresenceMenuItem.tsx | 2 +- .../navbar/workspace/WorkspaceMenuButton.tsx | 22 +++++------- .../navbar/workspace/WorkspacePreview.tsx | 34 +++++++++++++----- packages/sanity/src/ui/menuItem/MenuItem.tsx | 36 +++++++++++-------- 4 files changed, 58 insertions(+), 36 deletions(-) diff --git a/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenuItem.tsx b/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenuItem.tsx index 375e435bb41..a783c027a6c 100644 --- a/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenuItem.tsx +++ b/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenuItem.tsx @@ -70,7 +70,7 @@ export const PresenceMenuItem = memo(function PresenceMenuItem(props: PresenceLi ref={setMenuItemElement} text={presence.user.displayName} subText={hasLink ? undefined : 'Not in a document'} - avatar={ + preview={ - {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */} - {/* @ts-ignore - Workspace preview hasn't been redesigned for facelift */} - - + selected={workspace.name === activeWorkspace.name} + badgeText={STATE_TITLES[state]} + preview={} + text={workspace?.title || workspace.name} + subText={workspace?.subtitle} + /> ) })} diff --git a/packages/sanity/src/core/studio/components/navbar/workspace/WorkspacePreview.tsx b/packages/sanity/src/core/studio/components/navbar/workspace/WorkspacePreview.tsx index 8ea2e464a53..843f347ab74 100644 --- a/packages/sanity/src/core/studio/components/navbar/workspace/WorkspacePreview.tsx +++ b/packages/sanity/src/core/studio/components/navbar/workspace/WorkspacePreview.tsx @@ -4,15 +4,20 @@ import React, {createElement, isValidElement, useMemo} from 'react' import {isValidElementType} from 'react-is' import styled from 'styled-components' -const STATE_TITLES = { +export const STATE_TITLES = { 'logged-in': '', 'logged-out': 'Signed out', 'no-access': '', } -export const MediaCard = styled(Card)` - width: 35px; - height: 35px; +type PreviewIconSize = 'small' | 'large' +interface MediaCardProps { + $size: PreviewIconSize +} + +export const MediaCard = styled(Card)` + width: ${(props) => (props.$size === 'small' ? '35px' : '41px')}; + height: ${(props) => (props.$size === 'small' ? '35px' : '41px')}; svg { width: 100%; @@ -20,6 +25,22 @@ export const MediaCard = styled(Card)` } ` +export const WorkspacePreviewIcon = ({ + icon, + size = 'small', +}: { + icon: React.ComponentType | React.ReactNode + size: PreviewIconSize +}) => { + const iconComponent = useMemo(() => createIcon(icon), [icon]) + + return ( + + {iconComponent} + + ) +} + const createIcon = (icon: React.ComponentType | React.ReactNode) => { if (isValidElementType(icon)) return createElement(icon) if (isValidElement(icon)) return icon @@ -38,14 +59,11 @@ export interface WorkspacePreviewProps { export function WorkspacePreview(props: WorkspacePreviewProps) { const {state, subtitle, selected, title, icon, iconRight} = props - const iconComponent = useMemo(() => createIcon(icon), [icon]) const iconRightComponent = useMemo(() => createIcon(iconRight), [iconRight]) return ( - - {iconComponent} - + diff --git a/packages/sanity/src/ui/menuItem/MenuItem.tsx b/packages/sanity/src/ui/menuItem/MenuItem.tsx index f8d9e376698..18df0bfbdb3 100644 --- a/packages/sanity/src/ui/menuItem/MenuItem.tsx +++ b/packages/sanity/src/ui/menuItem/MenuItem.tsx @@ -10,12 +10,13 @@ import { } from '@sanity/ui' import React, {createElement, forwardRef, isValidElement, useMemo} from 'react' import {isValidElementType} from 'react-is' +import styled from 'styled-components' interface LargeMenuItem { size: 'large' subText?: string badgeText?: string - avatar?: React.ReactNode + preview?: React.ReactNode /** * Hotkeys are only supported in `size="small"` menu items. */ @@ -38,12 +39,12 @@ interface SmallMenuItem { */ badgeText?: undefined /** - * Avatar is only supported in `size="large"` menu items. + * preview is only supported in `size="large"` menu items. */ - avatar?: undefined + preview?: undefined } -const fontSize = 1 +const FONT_SIZE = 1 /** @internal */ export type MenuItemProps = Pick< @@ -67,6 +68,13 @@ export type MenuItemProps = Pick< children?: undefined } +const PreviewWrapper = styled.div` + width: 41px; + height: 41px; + display: flex; + align-items: center; + justify-content: center; +` /** * Studio UI . * @@ -81,7 +89,7 @@ export const MenuItem = forwardRef(function MenuItem( badgeText, subText, text, - avatar = null, + preview = null, icon, iconRight, hotkeys, @@ -94,23 +102,23 @@ export const MenuItem = forwardRef(function MenuItem( if (size === 'large') { return ( - {avatar} + {preview && {preview}} {(text || subText) && ( {text && ( - + {text} )} {subText && ( - + {subText} )} )} {badgeText && ( - + {badgeText} )} @@ -121,7 +129,7 @@ export const MenuItem = forwardRef(function MenuItem( return ( {icon && ( - + {isValidElement(icon) && icon} {isValidElementType(icon) && createElement(icon)} @@ -129,25 +137,25 @@ export const MenuItem = forwardRef(function MenuItem( {text && ( - + {text} )} {hotkeys && ( - + )} {iconRight && ( - + {isValidElement(iconRight) && iconRight} {isValidElementType(iconRight) && createElement(iconRight)} )} ) - }, [size, icon, text, hotkeys, iconRight, avatar, subText, badgeText]) + }, [size, icon, text, hotkeys, iconRight, preview, subText, badgeText]) return ( From 71ec3f9e4ba8dc4e9f27e2f1217f9544717a1cfb Mon Sep 17 00:00:00 2001 From: Pedro Bonamin Date: Thu, 2 Nov 2023 11:45:32 +0100 Subject: [PATCH 3/6] fix(facelift): support icon right in large menu item --- .../components/navbar/workspace/WorkspaceMenuButton.tsx | 5 ++++- packages/sanity/src/ui/menuItem/MenuItem.tsx | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/sanity/src/core/studio/components/navbar/workspace/WorkspaceMenuButton.tsx b/packages/sanity/src/core/studio/components/navbar/workspace/WorkspaceMenuButton.tsx index 0f6694b6cb9..9a09c26ce3d 100644 --- a/packages/sanity/src/core/studio/components/navbar/workspace/WorkspaceMenuButton.tsx +++ b/packages/sanity/src/core/studio/components/navbar/workspace/WorkspaceMenuButton.tsx @@ -1,4 +1,4 @@ -import {SelectIcon} from '@sanity/icons' +import {CheckmarkIcon, SelectIcon} from '@sanity/icons' import {Button, MenuButton, Menu, MenuButtonProps, Box, Label, Stack, Card} from '@sanity/ui' import React, {useCallback, useMemo, useState} from 'react' import styled from 'styled-components' @@ -101,6 +101,9 @@ export function WorkspaceMenuButton(props: WorkspaceMenuButtonProps) { onClick={handleSelectWorkspace} pressed={workspace.name === activeWorkspace.name} selected={workspace.name === activeWorkspace.name} + iconRight={ + workspace.name === activeWorkspace.name ? CheckmarkIcon : undefined + } badgeText={STATE_TITLES[state]} preview={} text={workspace?.title || workspace.name} diff --git a/packages/sanity/src/ui/menuItem/MenuItem.tsx b/packages/sanity/src/ui/menuItem/MenuItem.tsx index 18df0bfbdb3..4e63e13b7bd 100644 --- a/packages/sanity/src/ui/menuItem/MenuItem.tsx +++ b/packages/sanity/src/ui/menuItem/MenuItem.tsx @@ -122,6 +122,12 @@ export const MenuItem = forwardRef(function MenuItem( {badgeText} )} + {iconRight && ( + + {isValidElement(iconRight) && iconRight} + {isValidElementType(iconRight) && createElement(iconRight)} + + )} ) } From a610f7d9dcf82a0917695a0ebe853e3beecfbd52 Mon Sep 17 00:00:00 2001 From: Pedro Bonamin Date: Thu, 2 Nov 2023 12:18:15 +0100 Subject: [PATCH 4/6] feat(facelift): update StudioUI Menu item props, remove size --- .../PortableText/toolbar/BlockStyleSelect.tsx | 13 +- .../navbar/presence/PresenceMenuItem.tsx | 3 +- .../filter/inputs/string/StringList.tsx | 3 +- .../navbar/workspace/WorkspaceMenuButton.tsx | 13 +- packages/sanity/src/ui/menuItem/MenuItem.tsx | 125 ++++++------------ 5 files changed, 54 insertions(+), 103 deletions(-) diff --git a/packages/sanity/src/core/form/inputs/PortableText/toolbar/BlockStyleSelect.tsx b/packages/sanity/src/core/form/inputs/PortableText/toolbar/BlockStyleSelect.tsx index d04e56fe0ce..233a711d819 100644 --- a/packages/sanity/src/core/form/inputs/PortableText/toolbar/BlockStyleSelect.tsx +++ b/packages/sanity/src/core/form/inputs/PortableText/toolbar/BlockStyleSelect.tsx @@ -1,6 +1,14 @@ import React, {memo, useCallback, useMemo} from 'react' import {PortableTextEditor, usePortableTextEditor} from '@sanity/portable-text-editor' -import {Button, Menu, MenuButton, MenuButtonProps, Text} from '@sanity/ui' +import { + Button, + Menu, + MenuButton, + MenuButtonProps, + Text, + // eslint-disable-next-line no-restricted-imports + MenuItem, +} from '@sanity/ui' import {SelectIcon} from '@sanity/icons' import styled from 'styled-components' import { @@ -13,7 +21,6 @@ import { BlockQuote, Normal, } from '../text/textStyles' -import {MenuItem} from '../../../../../ui' import {useActiveStyleKeys, useFocusBlock} from './hooks' import {BlockStyleItem} from './types' @@ -146,8 +153,6 @@ export const BlockStyleSelect = memo(function BlockStyleSelect( // eslint-disable-next-line react/jsx-no-bind onClick={_disabled ? undefined : () => handleChange(item)} > - {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */} - {/* @ts-ignore: This item uses a specific render of the menu item.*/} {renderOption(item.style, item?.title || item.style)} ) diff --git a/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenuItem.tsx b/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenuItem.tsx index a783c027a6c..e5c60003a84 100644 --- a/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenuItem.tsx +++ b/packages/sanity/src/core/studio/components/navbar/presence/PresenceMenuItem.tsx @@ -63,13 +63,12 @@ export const PresenceMenuItem = memo(function PresenceMenuItem(props: PresenceLi return ( ) } diff --git a/packages/sanity/src/core/studio/components/navbar/workspace/WorkspaceMenuButton.tsx b/packages/sanity/src/core/studio/components/navbar/workspace/WorkspaceMenuButton.tsx index 9a09c26ce3d..5114dc6b01f 100644 --- a/packages/sanity/src/core/studio/components/navbar/workspace/WorkspaceMenuButton.tsx +++ b/packages/sanity/src/core/studio/components/navbar/workspace/WorkspaceMenuButton.tsx @@ -92,22 +92,19 @@ export function WorkspaceMenuButton(props: WorkspaceMenuButtonProps) { navigateUrl({path: workspace.basePath}) } } - + const isSelected = workspace.name === activeWorkspace.name return ( } text={workspace?.title || workspace.name} - subText={workspace?.subtitle} + subtitle={workspace?.subtitle} /> ) })} diff --git a/packages/sanity/src/ui/menuItem/MenuItem.tsx b/packages/sanity/src/ui/menuItem/MenuItem.tsx index 4e63e13b7bd..c266391850c 100644 --- a/packages/sanity/src/ui/menuItem/MenuItem.tsx +++ b/packages/sanity/src/ui/menuItem/MenuItem.tsx @@ -2,7 +2,6 @@ import { Flex, MenuItem as UIMenuItem, MenuItemProps as UIMenuItemProps, - Box, Text, Badge, Stack, @@ -12,58 +11,29 @@ import React, {createElement, forwardRef, isValidElement, useMemo} from 'react' import {isValidElementType} from 'react-is' import styled from 'styled-components' -interface LargeMenuItem { - size: 'large' - subText?: string - badgeText?: string - preview?: React.ReactNode - /** - * Hotkeys are only supported in `size="small"` menu items. - */ - hotkeys?: undefined - /** - * Icon is only supported in `size="small"` menu items. - */ - icon?: undefined -} - -interface SmallMenuItem { - size?: 'small' - hotkeys?: UIMenuItemProps['hotkeys'] - /** - * Sub text is only supported in `size="large"` menu items. - */ - subText?: undefined - /** - * Badge text is only supported in `size="large"` menu items. - */ - badgeText?: undefined - /** - * preview is only supported in `size="large"` menu items. - */ - preview?: undefined -} - const FONT_SIZE = 1 /** @internal */ export type MenuItemProps = Pick< UIMenuItemProps, - 'as' | 'icon' | 'iconRight' | 'pressed' | 'selected' | 'text' | 'tone' + 'as' | 'icon' | 'iconRight' | 'pressed' | 'selected' | 'text' | 'tone' | 'hotkeys' > & - (LargeMenuItem | SmallMenuItem) & Omit< React.HTMLProps, 'as' | 'height' | 'ref' | 'selected' | 'tabIndex' | 'size' > & { + subtitle?: string + badgeText?: string + /** + * Max allowed size is 41x41. + */ + preview?: React.ReactNode /** * Allows to add wrappers to the menu item, e.g. `Tooltip`. */ renderMenuItem?: (menuItem: React.JSX.Element) => React.ReactNode /** - * Usage of `children` is not recommended but still supported. - * Try using `renderMenuItem` instead. - * To use children opt out with `@ts-ignore`. + * Usage of `children` is not supported, import `MenuItem` from `@sanity/ui` instead. */ children?: undefined } @@ -74,6 +44,7 @@ const PreviewWrapper = styled.div` display: flex; align-items: center; justify-content: center; + overflow: hidden; ` /** * Studio UI . @@ -85,55 +56,22 @@ const PreviewWrapper = styled.div` */ export const MenuItem = forwardRef(function MenuItem( { - size = 'small', badgeText, - subText, + subtitle, text, preview = null, icon, iconRight, hotkeys, children, - ...props + renderMenuItem, + ...rest }: MenuItemProps, ref: React.Ref, ) { const menuItemContent = useMemo(() => { - if (size === 'large') { - return ( - - {preview && {preview}} - {(text || subText) && ( - - {text && ( - - {text} - - )} - {subText && ( - - {subText} - - )} - - )} - {badgeText && ( - - {badgeText} - - )} - {iconRight && ( - - {isValidElement(iconRight) && iconRight} - {isValidElementType(iconRight) && createElement(iconRight)} - - )} - - ) - } - return ( - + {icon && ( {isValidElement(icon) && icon} @@ -141,18 +79,33 @@ export const MenuItem = forwardRef(function MenuItem( )} - {text && ( - - - {text} - - + {preview && {preview}} + + {(text || subtitle) && ( + + {text && ( + + {text} + + )} + {subtitle && ( + + {subtitle} + + )} + )} {hotkeys && ( )} + {badgeText && ( + + {badgeText} + + )} + {iconRight && ( {isValidElement(iconRight) && iconRight} @@ -161,14 +114,12 @@ export const MenuItem = forwardRef(function MenuItem( )} ) - }, [size, icon, text, hotkeys, iconRight, preview, subText, badgeText]) + }, [icon, text, hotkeys, iconRight, preview, subtitle, badgeText]) return ( - - {/* Not recommended, should opt out with ts-ignore to use it. */} - {typeof children !== 'undefined' && children} - {typeof children === 'undefined' && typeof props.renderMenuItem === 'function' - ? props.renderMenuItem(menuItemContent) + + {typeof children === 'undefined' && typeof renderMenuItem === 'function' + ? renderMenuItem(menuItemContent) : menuItemContent} ) From 38ac8ad71b12401cffe3a777af548dea8ce75875 Mon Sep 17 00:00:00 2001 From: Pedro Bonamin Date: Thu, 2 Nov 2023 13:10:27 +0100 Subject: [PATCH 5/6] feat(facelift): add Studio UI menu item stories --- .../src/ui/__workshop__/MenuItemStory.tsx | 106 ++++++++++++++++++ packages/sanity/src/ui/__workshop__/index.ts | 14 +++ 2 files changed, 120 insertions(+) create mode 100644 packages/sanity/src/ui/__workshop__/MenuItemStory.tsx create mode 100644 packages/sanity/src/ui/__workshop__/index.ts diff --git a/packages/sanity/src/ui/__workshop__/MenuItemStory.tsx b/packages/sanity/src/ui/__workshop__/MenuItemStory.tsx new file mode 100644 index 00000000000..aca77242ccd --- /dev/null +++ b/packages/sanity/src/ui/__workshop__/MenuItemStory.tsx @@ -0,0 +1,106 @@ +import {CheckmarkIcon, CircleIcon, WarningOutlineIcon} from '@sanity/icons' +import {Avatar, Box, Card, Container, Menu, MenuDivider, Text, Flex} from '@sanity/ui' +import {useString} from '@sanity/ui-workshop' +import React from 'react' +import {MenuItem} from '../menuItem' +import {hues} from '@sanity/color' + +const HOTKEYS = ['Ctrl', 'Alt', 'P'] +const AVATAR_INITIALS = 'A.W.' + +export default function MenuItemStory() { + const subtitle = useString('Subtitle', 'Subtitle', 'Props') || '' + const text = useString('Text', 'Text', 'Props') || '' + + return ( + + + + + + + + } + text={text} + subtitle={subtitle} + /> + } + text={text} + subtitle={subtitle} + iconRight={CheckmarkIcon} + /> + } + text={text} + subtitle={subtitle} + badgeText="badge" + /> + + + Not recommended + + + Don't use left icons in large menu items + + + + + Don't use keyboard shortcuts with large menu items + + + + Don't use badges in small menu items + + + + Don't use icons and previews in the same item + + + } + text={text} + subtitle={subtitle} + /> + + Don't use icon right and hotkeys in the same item + + + + Don't use icon right and badge in the same item + + + + Don't use hotkeys and badge in the same item + + + + Don't use everything at once + + + } + subtitle={subtitle} + icon={CircleIcon} + text={text} + hotkeys={HOTKEYS} + badgeText={'badge'} + iconRight={CheckmarkIcon} + /> + + + + ) +} diff --git a/packages/sanity/src/ui/__workshop__/index.ts b/packages/sanity/src/ui/__workshop__/index.ts new file mode 100644 index 00000000000..37dc3793fec --- /dev/null +++ b/packages/sanity/src/ui/__workshop__/index.ts @@ -0,0 +1,14 @@ +import {defineScope} from '@sanity/ui-workshop' +import {lazy} from 'react' + +export default defineScope({ + name: 'studio-ui', + title: 'Studio UI', + stories: [ + { + name: 'menu-item', + title: 'MenuItem', + component: lazy(() => import('./MenuItemStory')), + }, + ], +}) From b19899e05fe856095fa1a39fb3319fc469069aba Mon Sep 17 00:00:00 2001 From: Pedro Bonamin Date: Thu, 2 Nov 2023 17:12:13 +0100 Subject: [PATCH 6/6] fix(tests): update snapshot --- packages/sanity/src/core/studio/Studio.test.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/sanity/src/core/studio/Studio.test.tsx b/packages/sanity/src/core/studio/Studio.test.tsx index cc19e7adeee..bec1b7f8b3d 100644 --- a/packages/sanity/src/core/studio/Studio.test.tsx +++ b/packages/sanity/src/core/studio/Studio.test.tsx @@ -43,9 +43,8 @@ describe('Studio', () => { const sheet = new ServerStyleSheet() try { const html = renderToStaticMarkup(sheet.collectStyles()) - expect(html).toMatchInlineSnapshot( - `"
Loading…
"`, + `"
Loading…
"`, ) } finally { sheet.seal() @@ -62,7 +61,7 @@ describe('Studio', () => { try { const html = renderToString(sheet.collectStyles()) expect(html).toMatchInlineSnapshot( - `"
Loading…
"`, + `"
Loading…
"`, ) } finally { sheet.seal() @@ -82,7 +81,7 @@ describe('Studio', () => { const html = renderToString(sheet.collectStyles()) node.innerHTML = html expect(html).toMatchInlineSnapshot( - `"
Loading…
"`, + `"
Loading…
"`, ) document.head.innerHTML += sheet.getStyleTags()