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

feat(react): image combobox #1477

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
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
Binary file added docs/assets/img/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/img/screenshot2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/img/screenshot3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions docs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ import '@fontsource/roboto/700.css';
import '@fontsource/lato';
import '@fontsource/pt-mono';

import screenshot from './assets/img/screenshot.png';
import screenshot2 from './assets/img/screenshot2.png';
import screenshot3 from './assets/img/screenshot3.png';

// styles
import '../packages/styles';
import '@deque/cauldron-react/cauldron.css';
Expand Down
13 changes: 13 additions & 0 deletions docs/pages/components/Combobox.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,19 @@ When autocomplete is set to "automatic" the listbox will provide a filtered list
</FieldWrap>
```

### Image variant

```jsx example
<FieldWrap>
<Combobox label="Image variant" variant="advanced">
<ComboboxOption value="None" description="Third screenshot" imageURL="/public/fonts/screenshot.png">None</ComboboxOption>
<ComboboxOption value="Manual" description="Second screenshot" imageURL="/public/fonts/screenshot2.png">Manual</ComboboxOption>
<ComboboxOption value="Automatic" description="Third screenshot" imageURL="/public/fonts/screenshot3.png">Automatic</ComboboxOption>
</Combobox>
</FieldWrap>
```


## Props

### Combobox
Expand Down
26 changes: 25 additions & 1 deletion packages/react/src/components/Combobox/Combobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ interface ComboboxOption {
value?: ComboboxValue;
formValue?: ComboboxValue;
description?: string;
imageURL?: string;
}

interface ComboboxProps
Expand All @@ -54,6 +55,7 @@ interface ComboboxProps
renderNoResults?: (() => JSX.Element) | React.ReactElement;
portal?: React.RefObject<HTMLElement> | HTMLElement;
inputRef?: React.Ref<HTMLInputElement>;
variant?: 'default' | 'advanced';
Copy link
Member

Choose a reason for hiding this comment

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

Why is this change necessary?

}

const defaultAutoCompleteMatches = (inputValue: string, value: string) => {
Expand All @@ -64,6 +66,21 @@ const defaultAutoCompleteMatches = (inputValue: string, value: string) => {
return value.toLowerCase().includes(inputValue.toLowerCase());
};

const advancedAutoCompleteMatches = (
inputValue: string,
value: string,
description?: string
) => {
// istanbul ignore if
if (!value || !description) {
return true;
}
return (
value.toLowerCase().includes(inputValue.toLowerCase()) ||
description.toLowerCase().includes(inputValue.toLowerCase())
);
};

const ComboboxNoResults = ({
children
}: {
Expand Down Expand Up @@ -98,6 +115,7 @@ const Combobox = forwardRef<HTMLDivElement, ComboboxProps>(
name,
renderNoResults,
portal,
variant,
inputRef: propInputRef = null,
'aria-describedby': ariaDescribedby,
...props
Expand All @@ -120,6 +138,7 @@ const Combobox = forwardRef<HTMLDivElement, ComboboxProps>(
const isControlled = typeof propValue !== 'undefined';
const isRequired = !!props.required;
const isAutoComplete = autocomplete !== 'none';
const isAdvancedCombobox = variant === 'advanced';
const hasError = !!error;

const comboboxOptions =
Expand Down Expand Up @@ -457,7 +476,12 @@ const Combobox = forwardRef<HTMLDivElement, ComboboxProps>(
inputValue={value}
formValue={formValue}
selectedValue={selectedValue}
matches={!isAutoComplete || defaultAutoCompleteMatches}
matches={
!isAutoComplete ||
(isAdvancedCombobox
? advancedAutoCompleteMatches
: defaultAutoCompleteMatches)
}
matchingOptions={matchingOptions}
setMatchingOptions={setMatchingOptions}
setFormValue={setFormValue}
Expand Down
8 changes: 5 additions & 3 deletions packages/react/src/components/Combobox/ComboboxContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type ComboboxContext = {
React.SetStateAction<Map<HTMLElement, ComboboxOptionState>>
>;
setFormValue: React.Dispatch<React.SetStateAction<ComboboxValue>>;
matches: (<T extends string = string>(value: T) => boolean) | boolean;
matches: (<T extends string = string>(...value: T[]) => boolean) | boolean;
};

export type ComboboxOptionState = {
Expand All @@ -21,7 +21,9 @@ export type ComboboxOptionState = {

type ComboboxProvider = {
children: React.ReactNode;
matches: ((inputValue: string, value: string) => boolean) | boolean;
matches:
| ((inputValue: string, value: string, description?: string) => boolean)
| boolean;
} & Omit<ComboboxContext, 'matches'>;

/* istanbul ignore next */
Expand Down Expand Up @@ -56,7 +58,7 @@ function ComboboxProvider({
selectedValue,
matches:
typeof matches === 'function' && !!inputValue
? (value) => matches(inputValue, value)
? (value, description) => matches(inputValue, value, description)
: true,
matchingOptions,
setMatchingOptions,
Expand Down
12 changes: 9 additions & 3 deletions packages/react/src/components/Combobox/ComboboxOption.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ interface ComboboxOptionProps extends React.HTMLAttributes<HTMLLIElement> {
disabled?: boolean;
value?: ComboboxValue;
formValue?: ComboboxValue;
description?: ContentNode;
description?: ContentNode | string;
children: string;
imageURL?: string;
isAdvancedCombobox?: boolean;
}

const ComboboxMatch = ({
Expand Down Expand Up @@ -64,6 +66,8 @@ const ComboboxOption = forwardRef<HTMLLIElement, ComboboxOptionProps>(
description,
value: propValue,
formValue,
imageURL,
isAdvancedCombobox = false,
...props
},
ref
Expand All @@ -83,7 +87,8 @@ const ComboboxOption = forwardRef<HTMLLIElement, ComboboxOptionProps>(
!!selected?.element && selected.element === comboboxOptionRef.current;
const isMatching =
(typeof matches === 'boolean' && matches) ||
(typeof matches === 'function' && matches(children));
(typeof matches === 'function' &&
matches(children, description as string));

// istanbul ignore next
useLayoutEffect(() => {
Expand Down Expand Up @@ -169,7 +174,8 @@ const ComboboxOption = forwardRef<HTMLLIElement, ComboboxOptionProps>(
<div className="ComboboxOption__description">{description}</div>
)}
</span>
{isSelected ? <Icon type="check-solid" /> : null}
{!imageURL && isSelected ? <Icon type="check-solid" /> : null}
{imageURL ? <img src={imageURL} alt={'Option description'} /> : null}
</ListboxOption>
);
}
Expand Down
5 changes: 5 additions & 0 deletions packages/styles/combobox.css
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,11 @@
border-radius: 50%;
}

.ComboboxOption img {
height: 6em;
width: 6em;
}

.ComboboxOption__match {
text-decoration: underline;
font-style: normal;
Expand Down
4 changes: 2 additions & 2 deletions webpack.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@ const config = {
]
},
{
test: /\.(eot|svg|ttf|woff|woff2)$/,
test: /\.(eot|svg|ttf|woff|woff2|png)$/,
type: 'asset/resource',
generator: {
filename: 'public/fonts/[name][ext]'
}
}
},
]
},
resolve: {
Expand Down
Loading