diff --git a/polaris-react/playground/DetailsPage.tsx b/polaris-react/playground/DetailsPage.tsx index 78d7ad1e4d1..3584f9f406d 100644 --- a/polaris-react/playground/DetailsPage.tsx +++ b/polaris-react/playground/DetailsPage.tsx @@ -44,9 +44,15 @@ import { TopBar, FooterHelp, Link, + Card, + BlockStack, + InlineGrid, + Icon, + Tooltip, } from '../src'; import type {DropZoneProps, PageProps} from '../src'; +import {Select as NewSelect} from './components'; import styles from './DetailsPage.module.scss'; export function DetailsPage() { @@ -549,7 +555,6 @@ export function DetailsPage() { // ---- Page markup ---- const actualPageMarkup = ( Success badge} @@ -626,25 +631,40 @@ export function DetailsPage() { - - - - - - - + + diff --git a/polaris-react/playground/components/Options/Options.tsx b/polaris-react/playground/components/Options/Options.tsx new file mode 100644 index 00000000000..92c1f6ba09a --- /dev/null +++ b/polaris-react/playground/components/Options/Options.tsx @@ -0,0 +1,111 @@ +import React, {useState, useCallback, useMemo} from 'react'; + +import {Tag, Listbox, Combobox, InlineStack} from '../../../src'; + +export function Options() { + const deselectedOptions = useMemo( + () => [ + {value: 'rustic', label: 'Rustic'}, + {value: 'antique', label: 'Antique'}, + {value: 'vinyl', label: 'Vinyl'}, + {value: 'vintage', label: 'Vintage'}, + {value: 'refurbished', label: 'Refurbished'}, + ], + [], + ); + + const [selectedOptions, setSelectedOptions] = useState([]); + const [inputValue, setInputValue] = useState(''); + const [options, setOptions] = useState(deselectedOptions); + + const updateText = useCallback( + (value: string) => { + setInputValue(value); + + if (value === '') { + setOptions(deselectedOptions); + return; + } + + const filterRegex = new RegExp(value, 'i'); + const resultOptions = deselectedOptions.filter((option) => + option.label.match(filterRegex), + ); + setOptions(resultOptions); + }, + [deselectedOptions], + ); + + const updateSelection = useCallback( + (selected: string) => { + if (selectedOptions.includes(selected)) { + setSelectedOptions( + selectedOptions.filter((option) => option !== selected), + ); + } else { + setSelectedOptions([...selectedOptions, selected]); + } + + updateText(''); + }, + [selectedOptions, updateText], + ); + + const removeTag = useCallback( + (tag: string) => () => { + const options = [...selectedOptions]; + options.splice(options.indexOf(tag), 1); + setSelectedOptions(options); + }, + [selectedOptions], + ); + + const tagsMarkup = selectedOptions.map((option) => ( + + {option} + + )); + + const optionsMarkup = + options.length > 0 + ? options.map((option) => { + const {label, value} = option; + + return ( + + {label} + + ); + }) + : null; + + return ( +
+ + } + > + {optionsMarkup ? ( + {optionsMarkup} + ) : null} + + + {tagsMarkup} +
+ ); +} diff --git a/polaris-react/playground/components/Select/Select.module.scss b/polaris-react/playground/components/Select/Select.module.scss new file mode 100644 index 00000000000..cd505a817a1 --- /dev/null +++ b/polaris-react/playground/components/Select/Select.module.scss @@ -0,0 +1,31 @@ +.Select { + padding: calc(var(--p-space-200) + var(--p-space-050)) var(--p-space-200) + var(--p-space-200) var(--p-space-300); + border-radius: var(--p-border-radius-200); + border: var(--p-border-width-0165) solid var(--p-color-input-border); + background-color: var(--Color-input-bg-surface); + width: 100%; + + &:focus:not(:active) { + outline: var(--p-border-width-050) solid var(--p-color-border-focus); + outline-offset: var(--p-border-width-025); + } + + &:hover { + border-color: var(--p-color-input-border-hover); + background-color: var(--Color-input-bg-surface-hover); + + svg { + fill: var(--p-color-icon-hover); + } + } + + &:active { + border-color: var(--p-color-input-border-active); + background-color: var(--Color-input-bg-surface-active); + + svg { + fill: var(--p-color-icon-active); + } + } +} diff --git a/polaris-react/playground/components/Select/Select.tsx b/polaris-react/playground/components/Select/Select.tsx new file mode 100644 index 00000000000..649223f4ec9 --- /dev/null +++ b/polaris-react/playground/components/Select/Select.tsx @@ -0,0 +1,67 @@ +import React from 'react'; +import {SelectMinor} from '@shopify/polaris-icons'; + +import type {OptionListProps} from '../../../src'; +import { + BlockStack, + Icon, + Text, + InlineGrid, + UnstyledButton, + Popover, +} from '../../../src'; +import {Options} from '../Options/Options'; + +import styles from './Select.module.scss'; + +interface Props { + title: string; + defaultSelected?: string[]; +} + +export function Select({ + title, + defaultSelected, + options, +}: Props & Pick) { + const [active, setActive] = React.useState(false); + // const [selected, setSelected] = React.useState(defaultSelected ?? []); + + const firstSelected = options?.find( + (option) => option.value === defaultSelected?.[0], + )?.label; + + const activator = ( + setActive((active) => !active)} + className={styles.Select} + > + + + + {title} + + {defaultSelected?.length && ( + + {firstSelected} + + )} + + + + + ); + + return ( + setActive(false)} + // TODO: Allow coverage of activator + // preferredAlignment="cover" + activator={activator} + > + + + ); +} diff --git a/polaris-react/playground/components/index.ts b/polaris-react/playground/components/index.ts new file mode 100644 index 00000000000..a153daafaf3 --- /dev/null +++ b/polaris-react/playground/components/index.ts @@ -0,0 +1,2 @@ +export * from './Select/Select'; +export * from './Options/Options'; diff --git a/polaris-react/src/components/Combobox/Combobox.tsx b/polaris-react/src/components/Combobox/Combobox.tsx index cdda0c4efc4..ca92dd43c8f 100644 --- a/polaris-react/src/components/Combobox/Combobox.tsx +++ b/polaris-react/src/components/Combobox/Combobox.tsx @@ -14,6 +14,8 @@ import type { ComboboxListboxType, ComboboxListboxOptionType, } from '../../utilities/combobox'; +import {BlockStack} from '../BlockStack'; +import {Box} from '../Box'; import styles from './Combobox.module.scss'; import {TextField} from './components'; @@ -31,6 +33,7 @@ export interface ComboboxProps { willLoadMoreOptions?: boolean; /** Height to set on the Popover Pane. */ height?: string; + persistent?: boolean; /** Callback fired when the bottom of the lisbox is reached. Use to lazy load when listbox option data is paginated. */ onScrolledToBottom?(): void; /** Callback fired when the popover closes */ @@ -44,6 +47,7 @@ export function Combobox({ preferredPosition = 'below', willLoadMoreOptions, height, + persistent, onScrolledToBottom, onClose, }: ComboboxProps) { @@ -148,7 +152,23 @@ export function Combobox({ ], ); - return ( + return persistent ? ( + <> + + + {activator} + + + + + +
{children}
+
+
+ + ) : (