Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(Select): expose generic types to allow by to work #2008

Merged
merged 2 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/components/Select/Select.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,19 @@ export const WithSelectedOption: StoryObj<typeof Select> = {
},
};

/**
* Use the `by` option to determine the selection (when using objects for the value list).
* - The type comparison can be by a named key in the object `by={'id'}` or using a comparison function
*
* See: https://headlessui.com/v1/react/listbox#listbox
*/
export const WithSelectedBy: StoryObj<typeof Select> = {
args: {
...WithSelectedOption.args,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[non-blocking] It might be a more thorough test to use defaultValue: {...exampleOptions[1]} instead of inheriting defaultValue: exampleOptions[1] from WithSelectedOption since the purpose of by is to handle cases where the values are copies rather than the exact same objects.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

++ good call. I suspect it will work properly, but this will prevent some by-ref getting thru down the road

by: 'key',
},
};

/**
* You can add a `name` prop to generate form fields for the value object.
*
Expand Down
12 changes: 9 additions & 3 deletions src/components/Select/Select.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Listbox } from '@headlessui/react';
import { Listbox, type ListboxProps } from '@headlessui/react';
import clsx from 'clsx';

import React, {
useContext,
useState,
type ReactNode,
type MouseEventHandler,
type ElementType,
} from 'react';
import { createPortal } from 'react-dom';
import { usePopper } from 'react-popper';
Expand All @@ -25,7 +26,12 @@ import Text from '../Text';

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

type SelectProps = ExtractProps<typeof Listbox> &
// https://github.com/tailwindlabs/headlessui/blob/%40headlessui/react%40v1.7.19/packages/%40headlessui-react/src/components/listbox/listbox.tsx#L349
type SelectProps = ListboxProps<
ElementType,
string | { [k: string]: unknown },
{ [k: string]: unknown }
> &
Comment on lines +32 to +33
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the first line represents what is allowed in things like defaultValue or value

the second line represents what gets used when by is evaluated

  • when a simple string, by is string
  • when an object, by is the type of the keys on that object (usually string | number), or that eval function
  • ...

PopoverOptions & {
// Component API
/**
Expand Down Expand Up @@ -235,7 +241,7 @@ export function Select({
labelLayout && styles[`select--label-layout-${labelLayout}`],
className,
);
const sharedProps = {
const sharedProps: SelectProps = {
className: componentClassName,
// Provide a wrapping <div> element for the select. This is needed so that any props
// passed directly to this component have a corresponding DOM element to receive them.
Expand Down
134 changes: 92 additions & 42 deletions src/components/Select/__snapshots__/Select.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@ exports[`<Select /> Generated Snapshots AdjustedWidth story renders snapshot 1`]
<label
class="label label--lg select__label"
data-headlessui-state="open"
id="headlessui-listbox-label-:r1k:"
id="headlessui-listbox-label-:r1q:"
>
Favorite Animal
</label>
</div>
<button
aria-controls="headlessui-listbox-options-:r1m:"
aria-controls="headlessui-listbox-options-:r1s:"
aria-expanded="true"
aria-haspopup="listbox"
aria-labelledby="headlessui-listbox-label-:r1k: headlessui-listbox-button-:r1l:"
aria-labelledby="headlessui-listbox-label-:r1q: headlessui-listbox-button-:r1r:"
class="select-button"
data-headlessui-state="open"
id="headlessui-listbox-button-:r1l:"
id="headlessui-listbox-button-:r1r:"
type="button"
>
<span
Expand Down Expand Up @@ -112,7 +112,7 @@ exports[`<Select /> Generated Snapshots Error story renders snapshot 1`] = `
<label
class="label label--lg select__label"
data-headlessui-state="open"
id="headlessui-listbox-label-:r26:"
id="headlessui-listbox-label-:r2c:"
>
Favorite Animal
</label>
Expand All @@ -123,13 +123,13 @@ exports[`<Select /> Generated Snapshots Error story renders snapshot 1`] = `
</span>
</div>
<button
aria-controls="headlessui-listbox-options-:r28:"
aria-controls="headlessui-listbox-options-:r2e:"
aria-expanded="true"
aria-haspopup="listbox"
aria-labelledby="headlessui-listbox-label-:r26: headlessui-listbox-button-:r27:"
aria-labelledby="headlessui-listbox-label-:r2c: headlessui-listbox-button-:r2d:"
class="select-button select-button--error"
data-headlessui-state="open"
id="headlessui-listbox-button-:r27:"
id="headlessui-listbox-button-:r2d:"
type="button"
>
<span
Expand Down Expand Up @@ -217,19 +217,19 @@ exports[`<Select /> Generated Snapshots MultipleWithTruncation story renders sna
<label
class="label label--lg select__label"
data-headlessui-state="open"
id="headlessui-listbox-label-:r1e:"
id="headlessui-listbox-label-:r1k:"
>
Favorite Animal(s)
</label>
</div>
<button
aria-controls="headlessui-listbox-options-:r1g:"
aria-controls="headlessui-listbox-options-:r1m:"
aria-expanded="true"
aria-haspopup="listbox"
aria-labelledby="headlessui-listbox-label-:r1e: headlessui-listbox-button-:r1f:"
aria-labelledby="headlessui-listbox-label-:r1k: headlessui-listbox-button-:r1l:"
class="select-button"
data-headlessui-state="open"
id="headlessui-listbox-button-:r1f:"
id="headlessui-listbox-button-:r1l:"
type="button"
>
<span
Expand Down Expand Up @@ -263,12 +263,12 @@ exports[`<Select /> Generated Snapshots NoVisibleLabel story renders snapshot 1`
data-testid="dropdown"
>
<button
aria-controls="headlessui-listbox-options-:r2j:"
aria-controls="headlessui-listbox-options-:r2p:"
aria-expanded="true"
aria-haspopup="listbox"
class="select-button"
data-headlessui-state="open"
id="headlessui-listbox-button-:r2i:"
id="headlessui-listbox-button-:r2o:"
type="button"
>
<span
Expand Down Expand Up @@ -306,17 +306,17 @@ exports[`<Select /> Generated Snapshots NoVisibleLabelButRequired story renders
<label
class="label label--lg select__label"
data-headlessui-state="open"
id="headlessui-listbox-label-:r2n:"
id="headlessui-listbox-label-:r2t:"
/>
</div>
<button
aria-controls="headlessui-listbox-options-:r2p:"
aria-controls="headlessui-listbox-options-:r2v:"
aria-expanded="true"
aria-haspopup="listbox"
aria-labelledby="headlessui-listbox-label-:r2n: headlessui-listbox-button-:r2o:"
aria-labelledby="headlessui-listbox-label-:r2t: headlessui-listbox-button-:r2u:"
class="select-button"
data-headlessui-state="open"
id="headlessui-listbox-button-:r2o:"
id="headlessui-listbox-button-:r2u:"
type="button"
>
<span
Expand Down Expand Up @@ -354,7 +354,7 @@ exports[`<Select /> Generated Snapshots Optional story renders snapshot 1`] = `
<label
class="label label--lg select__label"
data-headlessui-state="open"
id="headlessui-listbox-label-:r20:"
id="headlessui-listbox-label-:r26:"
>
Favorite Animal
</label>
Expand All @@ -365,13 +365,13 @@ exports[`<Select /> Generated Snapshots Optional story renders snapshot 1`] = `
</span>
</div>
<button
aria-controls="headlessui-listbox-options-:r22:"
aria-controls="headlessui-listbox-options-:r28:"
aria-expanded="true"
aria-haspopup="listbox"
aria-labelledby="headlessui-listbox-label-:r20: headlessui-listbox-button-:r21:"
aria-labelledby="headlessui-listbox-label-:r26: headlessui-listbox-button-:r27:"
class="select-button"
data-headlessui-state="open"
id="headlessui-listbox-button-:r21:"
id="headlessui-listbox-button-:r27:"
type="button"
>
<span
Expand Down Expand Up @@ -409,7 +409,7 @@ exports[`<Select /> Generated Snapshots Required story renders snapshot 1`] = `
<label
class="label label--lg select__label"
data-headlessui-state="open"
id="headlessui-listbox-label-:r1q:"
id="headlessui-listbox-label-:r20:"
>
Favorite Animal
</label>
Expand All @@ -420,13 +420,13 @@ exports[`<Select /> Generated Snapshots Required story renders snapshot 1`] = `
</span>
</div>
<button
aria-controls="headlessui-listbox-options-:r1s:"
aria-controls="headlessui-listbox-options-:r22:"
aria-expanded="true"
aria-haspopup="listbox"
aria-labelledby="headlessui-listbox-label-:r1q: headlessui-listbox-button-:r1r:"
aria-labelledby="headlessui-listbox-label-:r20: headlessui-listbox-button-:r21:"
class="select-button"
data-headlessui-state="open"
id="headlessui-listbox-button-:r1r:"
id="headlessui-listbox-button-:r21:"
type="button"
>
<span
Expand Down Expand Up @@ -459,12 +459,12 @@ exports[`<Select /> Generated Snapshots StyledUncontrolled story renders snapsho
data-testid="dropdown"
>
<button
aria-controls="headlessui-listbox-options-:r1a:"
aria-controls="headlessui-listbox-options-:r1g:"
aria-expanded="true"
aria-haspopup="listbox"
class="select-button"
data-headlessui-state="open"
id="headlessui-listbox-button-:r19:"
id="headlessui-listbox-button-:r1f:"
type="button"
>
<span
Expand Down Expand Up @@ -497,12 +497,12 @@ exports[`<Select /> Generated Snapshots UncontrolledHeadless story renders snaps
data-testid="dropdown"
>
<button
aria-controls="headlessui-listbox-options-:r15:"
aria-controls="headlessui-listbox-options-:r1b:"
aria-expanded="true"
aria-haspopup="listbox"
class="fpo"
data-headlessui-state="open"
id="headlessui-listbox-button-:r14:"
id="headlessui-listbox-button-:r1a:"
type="button"
>
🐶🐕🐩
Expand All @@ -522,7 +522,7 @@ exports[`<Select /> Generated Snapshots Warning story renders snapshot 1`] = `
<label
class="label label--lg select__label"
data-headlessui-state="open"
id="headlessui-listbox-label-:r2c:"
id="headlessui-listbox-label-:r2i:"
>
Favorite Animal
</label>
Expand All @@ -533,13 +533,13 @@ exports[`<Select /> Generated Snapshots Warning story renders snapshot 1`] = `
</span>
</div>
<button
aria-controls="headlessui-listbox-options-:r2e:"
aria-controls="headlessui-listbox-options-:r2k:"
aria-expanded="true"
aria-haspopup="listbox"
aria-labelledby="headlessui-listbox-label-:r2c: headlessui-listbox-button-:r2d:"
aria-labelledby="headlessui-listbox-label-:r2i: headlessui-listbox-button-:r2j:"
class="select-button select-button--warning"
data-headlessui-state="open"
id="headlessui-listbox-button-:r2d:"
id="headlessui-listbox-button-:r2j:"
type="button"
>
<span
Expand Down Expand Up @@ -577,19 +577,19 @@ exports[`<Select /> Generated Snapshots WithFieldName story renders snapshot 1`]
<label
class="label label--lg select__label"
data-headlessui-state="open"
id="headlessui-listbox-label-:ro:"
id="headlessui-listbox-label-:ru:"
>
Favorite Animal
</label>
</div>
<button
aria-controls="headlessui-listbox-options-:rq:"
aria-controls="headlessui-listbox-options-:r10:"
aria-expanded="true"
aria-haspopup="listbox"
aria-labelledby="headlessui-listbox-label-:ro: headlessui-listbox-button-:rp:"
aria-labelledby="headlessui-listbox-label-:ru: headlessui-listbox-button-:rv:"
class="select-button"
data-headlessui-state="open"
id="headlessui-listbox-button-:rp:"
id="headlessui-listbox-button-:rv:"
type="button"
>
<span
Expand Down Expand Up @@ -627,19 +627,19 @@ exports[`<Select /> Generated Snapshots WithFieldNote story renders snapshot 1`]
<label
class="label label--lg select__label"
data-headlessui-state="open"
id="headlessui-listbox-label-:ru:"
id="headlessui-listbox-label-:r14:"
>
Favorite Animal
</label>
</div>
<button
aria-controls="headlessui-listbox-options-:r10:"
aria-controls="headlessui-listbox-options-:r16:"
aria-expanded="true"
aria-haspopup="listbox"
aria-labelledby="headlessui-listbox-label-:ru: headlessui-listbox-button-:rv:"
aria-labelledby="headlessui-listbox-label-:r14: headlessui-listbox-button-:r15:"
class="select-button"
data-headlessui-state="open"
id="headlessui-listbox-button-:rv:"
id="headlessui-listbox-button-:r15:"
type="button"
>
<span
Expand All @@ -665,6 +665,56 @@ exports[`<Select /> Generated Snapshots WithFieldNote story renders snapshot 1`]
</div>
`;

exports[`<Select /> Generated Snapshots WithSelectedBy story renders snapshot 1`] = `
<div
class="select select--label-layout-vertical w-60"
data-headlessui-state="open"
data-testid="dropdown"
>
<div
class="select__overline"
>
<label
class="label label--lg select__label"
data-headlessui-state="open"
id="headlessui-listbox-label-:ro:"
>
Favorite Animal
</label>
</div>
<button
aria-controls="headlessui-listbox-options-:rq:"
aria-expanded="true"
aria-haspopup="listbox"
aria-labelledby="headlessui-listbox-label-:ro: headlessui-listbox-button-:rp:"
class="select-button"
data-headlessui-state="open"
id="headlessui-listbox-button-:rp:"
type="button"
>
<span
class=""
>
Cats
</span>
<svg
aria-hidden="true"
class="icon select-button__icon select-button__icon--reversed"
fill="currentColor"
height="1.5rem"
style="--icon-size: 1.5rem;"
viewBox="0 0 24 24"
width="1.5rem"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M15.88 9.29L12 13.17 8.12 9.29c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41l4.59 4.59c.39.39 1.02.39 1.41 0l4.59-4.59c.39-.39.39-1.02 0-1.41-.39-.38-1.03-.39-1.42 0z"
/>
</svg>
</button>
</div>
`;

exports[`<Select /> Generated Snapshots WithSelectedOption story renders snapshot 1`] = `
<div
class="select select--label-layout-vertical w-60"
Expand Down
Loading