Skip to content

Commit

Permalink
manage focus manually
Browse files Browse the repository at this point in the history
  • Loading branch information
kyledurand committed Mar 27, 2024
1 parent c0d1f53 commit d1c9a71
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 100 deletions.
22 changes: 2 additions & 20 deletions polaris-react/playground/DetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ import {
FooterHelp,
Link,
AlphaPicker,
Box,
Popover,
} from '../src';
import type {DropZoneProps, PageProps} from '../src';

Expand Down Expand Up @@ -644,7 +642,6 @@ export function DetailsPage() {
</LegacyCard>
</Layout.Section>
<Layout.Section variant="oneThird">
<Box minHeight="75vmin" />
<LegacyCard title="Organization">
<LegacyCard.Section>
<Select
Expand All @@ -654,22 +651,15 @@ export function DetailsPage() {
value={selected}
/>
<br />
<Select
label="Vendor"
options={options}
onChange={setSelected}
value={selected}
/>
<br />
<AlphaPicker
onSelect={handleSelect}
activator={{
label: 'Vendor',
placeholder: 'Search vendors',
placeholder: 'None selected',
}}
searchField={{
label: 'Search vendors',
placeholder: 'Search vendors',
placeholder: 'Search or add new vendor',
autoComplete: 'off',
value: query,
onChange: (value) => setQuery(value),
Expand All @@ -680,14 +670,6 @@ export function DetailsPage() {
children: `Add ${query}`,
}}
/>
<button>yohooo</button>
<Popover
active
activator={<button>click me</button>}
onClose={() => {}}
>
<p>hello</p>
</Popover>
</LegacyCard.Section>
<LegacyCard.Section title="Collections" />
<LegacyCard.Section title="Tags" />
Expand Down
1 change: 0 additions & 1 deletion polaris-react/src/components/Combobox/Combobox.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ export function Default() {
<Listbox onSelect={updateSelection}>{optionsMarkup}</Listbox>
) : null}
</Combobox>
<button>test</button>
</div>
);
}
Expand Down
29 changes: 15 additions & 14 deletions polaris-react/src/components/Picker/Picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,21 +58,23 @@ export function Picker({
onClose,
...listboxProps
}: PickerProps) {
const [query, setQuery] = useState<string>('');
const [filteredOptions, setFilteredOptions] = useState<OptionProps[] | null>(
options,
);
const activatorRef = React.createRef<HTMLButtonElement>();
const [activeItem, setActiveItem] = useState<string>();
const [popoverActive, setPopoverActive] = useState(false);
const [activeOptionId, setActiveOptionId] = useState<string>();
const [activeItem, setActiveItem] = useState<string>();
const [textFieldLabelId, setTextFieldLabelId] = useState<string>();
const [listboxId, setListboxId] = useState<string>();
const shouldOpen = !popoverActive;
const [query, setQuery] = useState<string>('');
const [filteredOptions, setFilteredOptions] = useState<OptionProps[] | null>(
options,
);

const shouldOpen = !popoverActive;
const handleClose = useCallback(() => {
setPopoverActive(false);
onClose?.();
}, [onClose]);
activatorRef.current?.focus();
}, [activatorRef, onClose]);

const handleOpen = useCallback(() => {
setPopoverActive(true);
Expand Down Expand Up @@ -164,27 +166,26 @@ export function Picker({
options.find((option) => option.value === activeItem)?.children,
);

const firstSelectedLabel = firstSelectedOption
? firstSelectedOption?.toString()
: activator.placeholder;

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

return (
<Popover
active={popoverActive}
activator={
<Activator
{...activator}
onClick={handleOpen}
placeholder={firstSelectedLabel}
selected={firstSelectedOption || ''}
placeholder={activator.placeholder}
ref={activatorRef}
/>
}
active={popoverActive}
autofocusTarget="none"
preferredPosition="cover"
onClose={handleClose}
preferredPosition="cover"
preventFocusOnClose
>
<Popover.Pane onScrolledToBottom={onScrolledToBottom} height={height}>
{searchField ? (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,49 +1,54 @@
import {SelectIcon} from '@shopify/polaris-icons';
import React from 'react';
import React, {forwardRef} from 'react';

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 {
disabled?: boolean;
label?: string;
placeholder?: string;
disabled?: boolean;
selected?: string;
onClick?(): void;
}

export function Activator({
disabled,
label,
placeholder,
onClick,
}: ActivatorProps) {
return (
<UnstyledButton
className={classNames(styles.Activator, disabled && styles.disabled)}
disabled={disabled}
onClick={onClick}
>
<BlockStack as="span" gap="100">
{label && (
<Text as="span" variant="bodySm" alignment="start" tone="subdued">
{label}
</Text>
)}
export const Activator = forwardRef<HTMLButtonElement, ActivatorProps>(
({disabled, label, placeholder, selected, onClick}, ref) => {
return (
<button
ref={ref}
disabled={disabled}
onClick={onClick}
className={classNames(styles.Activator, disabled && styles.disabled)}
>
<BlockStack as="span" gap="100">
{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} />
</span>
</UnstyledButton>
);
}
{(selected !== '' || placeholder) && (
<Text
as="span"
variant="bodyMd"
alignment="start"
tone={selected ? undefined : 'subdued'}
>
{selected || placeholder}
</Text>
)}
</BlockStack>
<span>
<Icon tone="subdued" source={SelectIcon} />
</span>
</button>
);
},
);

Activator.displayName = 'Activator';
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React from 'react';
import {mountWithApp} from 'tests/utilities';

import {Activator} from '../Activator';
import {UnstyledButton} from '../../../../UnstyledButton';

describe('<Activator />', () => {
it('renders a label', () => {
Expand All @@ -20,6 +19,6 @@ describe('<Activator />', () => {
it('renders a disabled activator', () => {
const activator = mountWithApp(<Activator disabled />);

expect(activator).toContainReactComponent(UnstyledButton, {disabled: true});
expect(activator).toContainReactComponent('button', {disabled: true});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {Label, labelID} from '../../../Label';
import type {TextFieldProps} from '../../../TextField';
import {useComboboxTextField} from '../../../../utilities/combobox';
import {InlineStack} from '../../../InlineStack';
import {Text} from '../../../Text';

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

Expand All @@ -15,7 +16,6 @@ export function SearchField({
onBlur,
onChange,
label,
labelHidden,
prefix,
placeholder,
focused,
Expand Down Expand Up @@ -76,30 +76,30 @@ export function SearchField({
}

return (
<>
<Label id={textFieldId} hidden={labelHidden}>
{label}
</Label>
<InlineStack gap="100" blockAlign="center">
<InlineStack gap="100" blockAlign="center">
<Label id={textFieldId}>
<Text as="span" visuallyHidden>
{label}
</Text>
<span>{prefix}</span>
<input
ref={inputRef}
className={styles.SearchField}
value={value}
id={textFieldId}
type={type}
aria-activedescendant={activeOptionId}
role="combobox"
aria-haspopup="listbox"
aria-autocomplete="list"
aria-expanded="true"
placeholder={placeholder}
aria-controls={listboxId}
onFocus={handleFocus}
onBlur={handleBlur}
onChange={({target}) => handleChange(target.value, textFieldId)}
/>
</InlineStack>
</>
</Label>
<input
ref={inputRef}
id={textFieldId}
className={styles.SearchField}
value={value}
type={type}
aria-activedescendant={activeOptionId}
role="combobox"
aria-haspopup="listbox"
aria-autocomplete="list"
aria-expanded="true"
placeholder={placeholder}
aria-controls={listboxId}
onFocus={handleFocus}
onBlur={handleBlur}
onChange={({target}) => handleChange(target.value, textFieldId)}
/>
</InlineStack>
);
}
9 changes: 4 additions & 5 deletions polaris-react/src/components/Picker/tests/Picker.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {mountWithApp} from 'tests/utilities';

import {Picker} from '../Picker';
import {Activator, SearchField} from '../components';
import {UnstyledButton} from '../../UnstyledButton';
import {Listbox} from '../../Listbox';

describe('<Picker />', () => {
Expand All @@ -26,7 +25,7 @@ describe('<Picker />', () => {
/>,
);

picker.find(UnstyledButton)!.trigger('onClick');
picker.find('button')!.trigger('onClick');

expect(picker).toContainReactComponent(SearchField);
});
Expand All @@ -39,7 +38,7 @@ describe('<Picker />', () => {

const picker = mountWithApp(<Picker activator={{}} options={options} />);

picker.find(UnstyledButton)!.trigger('onClick');
picker.find('button')!.trigger('onClick');

expect(picker).toContainReactComponent(Listbox);
expect(picker).toContainReactComponentTimes(Listbox.Option, options.length);
Expand All @@ -56,7 +55,7 @@ describe('<Picker />', () => {
/>,
);

picker.find(UnstyledButton)!.trigger('onClick');
picker.find('button')!.trigger('onClick');
picker.find(SearchField)!.trigger('onChange', 'Add');

expect(picker).toContainReactComponent(Listbox.Action);
Expand All @@ -76,7 +75,7 @@ describe('<Picker />', () => {
/>,
);

picker.find(UnstyledButton)!.trigger('onClick');
picker.find('button')!.trigger('onClick');
expect(picker).toContainReactComponentTimes(Listbox.Option, options.length);

picker.find(SearchField)!.trigger('onChange', 'One');
Expand Down

0 comments on commit d1c9a71

Please sign in to comment.