-
Notifications
You must be signed in to change notification settings - Fork 6
Search #94
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
Search #94
Changes from all commits
0b2c689
729302f
5a16e05
89dce99
1165e48
7798590
e00a1a9
1f307bd
10e843c
bfe91ec
c9c9286
e3bb849
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| import PropTypes from 'prop-types'; | ||
| import React, { useState, useRef } from 'react'; | ||
| import Search from './index'; | ||
| import SearchAssistance from './SearchAssistance'; | ||
| import { wcBool } from '../utils'; | ||
| import { POSITIONS } from '../constants'; | ||
| import useClickOutside from 'use-onclickoutside'; | ||
|
|
||
| /** | ||
| * @see https://helixdesignsystem.github.io/helix-ui/components/search/ | ||
| */ | ||
| const SearchAssist = ({ children, onFocus, onBlur, position, ...rest }) => { | ||
| const [open, setOpen] = useState(false); | ||
|
|
||
| const searchRef = useRef(); | ||
| useClickOutside(searchRef, () => setOpen(false)); | ||
|
|
||
| const hasChildren = React.Children.toArray(children).filter((c) => c).length > 0; | ||
|
|
||
| return ( | ||
| <div ref={searchRef}> | ||
| <Search | ||
| {...rest} | ||
| onFocus={(e) => { | ||
| setOpen(true); | ||
| onFocus && onFocus(e); | ||
| }} | ||
| wrapperId={`${rest.id}-hx-search-control`} | ||
| /> | ||
| {hasChildren && ( | ||
| <SearchAssistance | ||
| relativeTo={`${rest.id}-hx-search-control`} | ||
| open={wcBool(open)} | ||
| position={position} | ||
| onClick={() => setOpen(false)} | ||
| > | ||
| {children} | ||
| </SearchAssistance> | ||
| )} | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| SearchAssist.propTypes = { | ||
| children: PropTypes.node.isRequired, | ||
| position: PropTypes.oneOf(POSITIONS), | ||
| className: PropTypes.string, | ||
| clearLabel: PropTypes.string, | ||
| label: PropTypes.string, | ||
| id: PropTypes.string.isRequired, | ||
| wrapperId: PropTypes.string, | ||
| disabled: PropTypes.bool, | ||
| onChange: PropTypes.func, | ||
| onFocus: PropTypes.func, | ||
| onBlur: PropTypes.func, | ||
| onClear: PropTypes.func, | ||
| optional: PropTypes.bool, | ||
| required: PropTypes.bool, | ||
| }; | ||
|
|
||
| export default SearchAssist; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import PropTypes from 'prop-types'; | ||
| import React from 'react'; | ||
| import { POSITIONS } from '../constants'; | ||
|
|
||
| /** | ||
| * @see https://helixdesignsystem.github.io/helix-ui/elements/hx-search-assistance/ | ||
100stacks marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| */ | ||
| const SearchAssistance = ({ children, className, relativeTo, ...rest }) => { | ||
| return ( | ||
| <hx-search-assistance class={className} relative-to={relativeTo} {...rest}> | ||
| {children} | ||
| </hx-search-assistance> | ||
| ); | ||
| }; | ||
|
|
||
| SearchAssistance.propTypes = { | ||
| children: PropTypes.node.isRequired, | ||
| className: PropTypes.string, | ||
| relativeTo: PropTypes.string.isRequired, | ||
nicko-winner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| position: PropTypes.oneOf(POSITIONS), | ||
| }; | ||
|
|
||
| export default SearchAssistance; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| import classnames from 'classnames'; | ||
| import PropTypes from 'prop-types'; | ||
| import React from 'react'; | ||
| import Icon from '../Icon'; | ||
| import { wcBool } from '../utils'; | ||
|
|
||
| /** | ||
| * @see https://helixdesignsystem.github.io/helix-ui/components/search/ | ||
| */ | ||
| const Search = ({ | ||
| children, | ||
| disabled, | ||
| id, | ||
| label, | ||
| className, | ||
| clearLabel, | ||
| onClear, | ||
| optional, | ||
| required, | ||
| wrapperId, | ||
| ...rest | ||
| }) => { | ||
| return ( | ||
| <hx-search-control class={className} id={wrapperId}> | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we have prop called |
||
| <input id={id} {...rest} disabled={wcBool(disabled)} type="search" /> | ||
| <button type="button" className="hxClear" aria-label={clearLabel} hidden onClick={onClear}> | ||
| <Icon type="times" /> | ||
| </button> | ||
| <hx-search></hx-search> | ||
| {label && ( | ||
| <label | ||
| className={classnames({ | ||
| hxOptional: optional, | ||
| hxRequired: required, | ||
| })} | ||
| htmlFor={id} | ||
| > | ||
| {label} | ||
| </label> | ||
| )} | ||
| </hx-search-control> | ||
| ); | ||
| }; | ||
|
|
||
| Search.propTypes = { | ||
| className: PropTypes.string, | ||
| clearLabel: PropTypes.string, | ||
| label: PropTypes.string, | ||
| id: PropTypes.string.isRequired, | ||
| wrapperId: PropTypes.string, | ||
| disabled: PropTypes.bool, | ||
| onChange: PropTypes.func, | ||
| onFocus: PropTypes.func, | ||
| onBlur: PropTypes.func, | ||
| onClear: PropTypes.func, | ||
| optional: PropTypes.bool, | ||
| required: PropTypes.bool, | ||
| }; | ||
|
|
||
| Search.defaultProps = { | ||
| clearLabel: 'Clear search', | ||
| }; | ||
|
|
||
| export default Search; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| import { action } from '@storybook/addon-actions'; | ||
| import { boolean, text, select } from '@storybook/addon-knobs'; | ||
| import { storiesOf } from '@storybook/react'; | ||
| import React, { useState } from 'react'; | ||
| import Search from '../Search'; | ||
| import SearchAssist from './SearchAssist'; | ||
| import { InputContainer } from '../storyUtils'; | ||
| import { POSITIONS } from '../constants'; | ||
|
|
||
| storiesOf('Search', module) | ||
| .add('All Knobs', () => { | ||
| let disabled = boolean('disabled', false); | ||
| let label = text('label', ''); | ||
| let optional = boolean('optional', false); | ||
| let required = boolean('required', false); | ||
| let position = select('positions', POSITIONS, 'bottom-center'); | ||
|
|
||
| return ( | ||
| <InputContainer> | ||
| <Search | ||
| id="my-search" | ||
| {...(disabled && { disabled })} | ||
| {...(label && { label })} | ||
| {...(optional && { optional })} | ||
| {...(required && { required })} | ||
| {...(position && { position })} | ||
| onChange={action('change')} | ||
| autocomplete="off" | ||
| /> | ||
| </InputContainer> | ||
| ); | ||
| }) | ||
| .add('SearchAssist', () => { | ||
| let disabled = boolean('disabled', false); | ||
| let label = text('label', 'Search'); | ||
| let optional = boolean('optional', false); | ||
| let required = boolean('required', false); | ||
| let position = select('positions', POSITIONS, 'bottom-center'); | ||
| const [value, setValue] = useState(''); | ||
|
|
||
| return ( | ||
| <InputContainer> | ||
| <SearchAssist | ||
| id="my-assisted-search" | ||
| onChange={(e) => setValue(e.target.value)} | ||
| value={value} | ||
| onClear={(e) => { | ||
| action('onClear'); | ||
| setValue(''); | ||
| }} | ||
| onFocus={(e) => action('onFocus')} | ||
| onBlur={(e) => action('onBlur')} | ||
| autocomplete="off" | ||
| {...(disabled && { disabled })} | ||
| {...(label && { label })} | ||
| {...(optional && { optional })} | ||
| {...(required && { required })} | ||
| {...(position && { position })} | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is an issue in the helix positioning logic. We are sending the correct props. The storybook controls on the bottom for knobs are pushing up against the bottom of the search suggestion box. I think this issue exists for all helix positionable elements. Examples from official helix docs: Popover is a good place to reproduce this issue inside helix: make your window smaller so helix places the popover, on top, then choose the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok makes sense, I see it's the size of the Storybook preview pane for me. Yes, that's the default behavior for HelixUI components with the positioning logic.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Btw what are your thoughts the positioning behavior? Any tweaks needed?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the fix in helix might look like this: Before trying to reposition an item make sure there is more space in the direction the menu wants to reposition in. Maybe only trigger the reposition if there is at least 50-100px more space in the direction it wants to reposition into. If there is not, then keep its existing position. That may help menus from prepositioning from a bad fit to an unintended worse fit. Or maybe there is an option to disable position changing for use cases that do not need that behavior?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's really great feedback. Most of our use cases were based on users scrolling, and the direction they are scrolling. Here's the collision detection logic: Good suggestion for allowing implementors an option to disable the collision detection. |
||
| > | ||
| <header>Search for "{value}"</header> | ||
| <section> | ||
| <header>Category Header</header> | ||
| {POSITIONS.filter((p) => p.search(value) !== -1).map((item) => ( | ||
| <button className="hxSearchSuggestion" key={item} onClick={() => setValue(item)}> | ||
| Here is a possible match: {item} | ||
| </button> | ||
| ))} | ||
| </section> | ||
| </SearchAssist> | ||
| </InputContainer> | ||
| ); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,20 +1,26 @@ | ||
| /* Export helix-react definition */ | ||
| import Button from './Button'; | ||
| import Alert from './Alert'; | ||
| import Button from './Button'; | ||
| import ChoiceTile from './ChoiceTile'; | ||
| import Drawer from './Drawer'; | ||
| import Icon from './Icon'; | ||
| import Modal from './Modal'; | ||
| import Popover from './Popover'; | ||
nicko-winner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| import Tooltip from './Tooltip'; | ||
| import Select from './Select'; | ||
| import Search from './Search'; | ||
| import SearchAssist from './Search/SearchAssist'; | ||
|
|
||
| export default { | ||
| Button, | ||
| Alert, | ||
| Button, | ||
| ChoiceTile, | ||
| Drawer, | ||
| Icon, | ||
| Modal, | ||
| Popover, | ||
nicko-winner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Select, | ||
| Tooltip, | ||
| Search, | ||
| SearchAssist | ||
| }; | ||




Uh oh!
There was an error while loading. Please reload this page.