Skip to content

Commit

Permalink
add disabled state, show first option text if react node
Browse files Browse the repository at this point in the history
  • Loading branch information
kyledurand committed Mar 16, 2024
1 parent 7fd7213 commit 1638997
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 29 deletions.
13 changes: 11 additions & 2 deletions polaris-react/playground/Playground.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import {PlusCircleIcon} from '@shopify/polaris-icons';

import {BlockStack, Card, Layout, Page, Picker, Text} from '../src';

Expand Down Expand Up @@ -54,11 +55,19 @@ export function Playground() {
label: 'Search for a product',
placeholder: 'Search for a product',
autoComplete: 'off',
onChange: (value) => console.log(value),
}}
activator={{
label: 'Product',
placeholder: 'Select a product',
}}
activator={{label: 'Product', placeholder: 'Select a product'}}
options={options}
onSelect={(selected) => console.log(selected)}
addAction={{value: 'add', children: 'Add product'}}
addAction={{
value: 'addy',
children: 'Add product',
icon: PlusCircleIcon,
}}
/>
</BlockStack>
</Card>
Expand Down
31 changes: 17 additions & 14 deletions polaris-react/src/components/Picker/Picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,23 @@ import {Box} from '../Box';
import type {TextFieldProps} from '../TextField';
import type {ListboxProps, OptionProps} from '../Listbox';
import {Listbox} from '../Listbox';
import type {IconProps} from '../Icon';
import {Icon} from '../Icon';

import {TextField, Activator} from './components';
import {Activator, TextField} from './components';
import type {ActivatorProps} from './components';

export interface PickerProps extends Omit<ListboxProps, 'children'> {
/** Configure the button that activates the Picker */
activator: ActivatorProps;
/** Textfield that allows filtering of options */
searchField?: TextFieldProps;
/** Allows more than one option to be selected */
allowMultiple?: boolean;
/** The options to be listed within the picker */
options?: OptionProps[];
/** Used to add a new picker option that isn't listed */
addAction?: OptionProps;
addAction?: OptionProps & {icon?: IconProps['source']};
/** Textfield that allows filtering of options */
searchField?: TextFieldProps;
/** Whether or not more options are available to lazy load when the bottom of the listbox reached. Use the hasMoreResults boolean provided by the GraphQL API of the paginated data. */
willLoadMoreOptions?: boolean;
/** Height to set on the Popover Pane. */
Expand All @@ -49,10 +50,8 @@ export interface PickerProps extends Omit<ListboxProps, 'children'> {
onClose?(): void;
}

const filterRegex = (value: string) => new RegExp(value, 'i');

// regex match string exact upper or lower case
const filterRegexExact = (value: string) => new RegExp(`^${value}$`, 'i');
const FILTER_REGEX = (value: string) => new RegExp(value, 'i');
const QUERY_REGEX = (value: string) => new RegExp(`^${value}$`, 'i');

export function Picker({
activator,
Expand Down Expand Up @@ -182,22 +181,22 @@ export function Picker({
}

const resultOptions = options?.filter((option) =>
filterRegex(value).exec(reactChildrenText(option.children)),
FILTER_REGEX(value).exec(reactChildrenText(option.children)),
);
setFilteredOptions(resultOptions ?? []);
},
[options],
);

const firstSelectedOption = options.find(
(option) => option.value === active,
)?.children;
const firstSelectedOption = reactChildrenText(
options.find((option) => option.value === active)?.children,
);
const firstSelectedLabel = firstSelectedOption
? firstSelectedOption?.toString()
: activator.placeholder;

const queryMatchesExistingOption = options.some((option) =>
filterRegexExact(query).exec(reactChildrenText(option.children)),
QUERY_REGEX(query).exec(reactChildrenText(option.children)),
);

return (
Expand Down Expand Up @@ -229,11 +228,15 @@ export function Picker({
>
<ComboboxTextFieldContext.Provider value={textFieldContextValue}>
<TextField
{...searchField}
autoComplete="off"
label={searchField.label}
value={query}
placeholder={searchField.placeholder}
onChange={updateText}
onChange={(value) => {
updateText(value);
searchField.onChange?.(value, '');
}}
prefix={<Icon source={SearchIcon} />}
labelHidden
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,9 @@
outline-offset: var(--p-space-025);
}
}

.disabled {
pointer-events: none;
background-color: var(--p-color-bg-surface-disabled);
border-color: var(--p-color-border-disabled);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,40 @@ import {BlockStack} from '../../../BlockStack';
import {Icon} from '../../../Icon';
import {Text} from '../../../Text';
import {UnstyledButton} from '../../../UnstyledButton';
import {classNames} from '../../../../utilities/css';

import styles from './Activator.module.css';

export interface ActivatorProps {
label: string;
label?: string;
placeholder?: string;
disabled?: boolean;
onClick?(): void;
}

export function Activator({placeholder, onClick, label}: ActivatorProps) {
export function Activator({
disabled,
label,
placeholder,
onClick,
}: ActivatorProps) {
return (
<UnstyledButton className={styles.Activator} onClick={onClick}>
<UnstyledButton
className={classNames(styles.Activator, disabled && styles.disabled)}
onClick={onClick}
>
<BlockStack as="span" gap="100">
<Text as="span" variant="bodySm" alignment="start" tone="subdued">
{label}
</Text>
<Text as="span" variant="bodyMd" alignment="start">
{placeholder}
</Text>
{label && (
<Text as="span" variant="bodySm" alignment="start" tone="subdued">
{label}
</Text>
)}

{placeholder && (
<Text as="span" variant="bodyMd" alignment="start">
{placeholder}
</Text>
)}
</BlockStack>
<span>
<Icon tone="subdued" source={SelectIcon} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export function calculateVerticalPosition(
fixed: boolean | undefined,
topBarOffset = 0,
) {
const log = console.log;
const activatorTop = activatorRect.top;
const activatorBottom = activatorTop + activatorRect.height;
const spaceAbove = activatorRect.top - topBarOffset;
Expand Down Expand Up @@ -102,7 +101,6 @@ export function calculateVerticalPosition(
}

if (preferredPosition === 'cover') {
log('is covered');
return (enoughSpaceFromBottomScroll ||
(distanceToBottomScroll >= distanceToTopScroll &&
!enoughSpaceFromTopScroll)) &&
Expand All @@ -111,11 +109,11 @@ export function calculateVerticalPosition(
? positionIfCoverBelow
: positionIfCoverAbove;
}
log('is not covered');

if (enoughSpaceFromTopScroll && enoughSpaceFromBottomScroll) {
return spaceAbove > spaceBelow ? positionIfAbove : positionIfBelow;
}
log('is super not covered');

return distanceToTopScroll > minimumSpaceToScroll
? positionIfAbove
: positionIfBelow;
Expand Down

0 comments on commit 1638997

Please sign in to comment.