diff --git a/package.json b/package.json
index 600c991..0a07e76 100644
--- a/package.json
+++ b/package.json
@@ -53,7 +53,8 @@
"react-dom": "^16.6.3"
},
"dependencies": {
- "classnames": "^2.2.6"
+ "classnames": "^2.2.6",
+ "use-onclickoutside": "^0.3.1"
},
"husky": {
"hooks": {
diff --git a/src/Choice Tile/index.js b/src/ChoiceTile/index.js
similarity index 96%
rename from src/Choice Tile/index.js
rename to src/ChoiceTile/index.js
index 269d45b..88e2ef2 100644
--- a/src/Choice Tile/index.js
+++ b/src/ChoiceTile/index.js
@@ -52,7 +52,7 @@ ChoiceTile.propTypes = {
invalid: PropTypes.bool,
name: PropTypes.string.isRequired,
onChange: PropTypes.func,
- size: PropTypes.oneOf(SIZES),
+ size: PropTypes.oneOf(Object.keys(SIZES)),
subdued: PropTypes.bool,
title: PropTypes.string.isRequired,
};
diff --git a/src/Choice Tile/stories.js b/src/ChoiceTile/stories.js
similarity index 100%
rename from src/Choice Tile/stories.js
rename to src/ChoiceTile/stories.js
diff --git a/src/Drawer/index.js b/src/Drawer/index.js
index f8eef93..2b08614 100644
--- a/src/Drawer/index.js
+++ b/src/Drawer/index.js
@@ -20,7 +20,7 @@ Drawer.propTypes = {
onClose: PropTypes.func,
onOpen: PropTypes.func,
open: PropTypes.bool,
- size: PropTypes.oneOf(SIZES),
+ size: PropTypes.oneOf(Object.keys(SIZES)),
};
Drawer.defaultProps = {
diff --git a/src/Modal/index.js b/src/Modal/index.js
index 9d2e8c5..f6a5dee 100644
--- a/src/Modal/index.js
+++ b/src/Modal/index.js
@@ -16,7 +16,7 @@ const Modal = ({ onOpen, onClose, className, open, size, children, ...rest }) =>
Modal.propTypes = {
id: PropTypes.string,
- size: PropTypes.oneOf(SIZES),
+ size: PropTypes.oneOf(Object.keys(SIZES)),
children: PropTypes.node.isRequired,
className: PropTypes.string,
open: PropTypes.bool,
diff --git a/src/Search/SearchAssist.js b/src/Search/SearchAssist.js
new file mode 100644
index 0000000..09e7a15
--- /dev/null
+++ b/src/Search/SearchAssist.js
@@ -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 (
+
+ {
+ setOpen(true);
+ onFocus && onFocus(e);
+ }}
+ wrapperId={`${rest.id}-hx-search-control`}
+ />
+ {hasChildren && (
+ setOpen(false)}
+ >
+ {children}
+
+ )}
+
+ );
+};
+
+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;
diff --git a/src/Search/SearchAssistance.js b/src/Search/SearchAssistance.js
new file mode 100644
index 0000000..8175083
--- /dev/null
+++ b/src/Search/SearchAssistance.js
@@ -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/
+ */
+const SearchAssistance = ({ children, className, relativeTo, ...rest }) => {
+ return (
+
+ {children}
+
+ );
+};
+
+SearchAssistance.propTypes = {
+ children: PropTypes.node.isRequired,
+ className: PropTypes.string,
+ relativeTo: PropTypes.string.isRequired,
+ position: PropTypes.oneOf(POSITIONS),
+};
+
+export default SearchAssistance;
diff --git a/src/Search/index.js b/src/Search/index.js
new file mode 100644
index 0000000..42f8e59
--- /dev/null
+++ b/src/Search/index.js
@@ -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 (
+
+
+
+
+ {label && (
+
+ )}
+
+ );
+};
+
+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;
diff --git a/src/Search/stories.js b/src/Search/stories.js
new file mode 100644
index 0000000..5859769
--- /dev/null
+++ b/src/Search/stories.js
@@ -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 (
+
+
+
+ );
+ })
+ .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 (
+
+ 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 })}
+ >
+
+
+
+ {POSITIONS.filter((p) => p.search(value) !== -1).map((item) => (
+
+ ))}
+
+
+
+ );
+ });
diff --git a/src/index.mjs b/src/index.mjs
index 779609e..de07366 100644
--- a/src/index.mjs
+++ b/src/index.mjs
@@ -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';
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,
Select,
Tooltip,
+ Search,
+ SearchAssist
};
diff --git a/src/storyUtils.js b/src/storyUtils.js
index bf6075a..85b6dfc 100644
--- a/src/storyUtils.js
+++ b/src/storyUtils.js
@@ -1,3 +1,5 @@
+import React from 'react';
+
export const getShortText = () => `lorem ipsum dolor sir amet`;
export const getLongText = () => `
@@ -13,3 +15,5 @@ export const getLongText = () => `
elementum integer enim neque volutpat. Etiam sit amet nisl purus in mollis nunc. Diam sit amet
nisl suscipit. Nulla pharetra diam sit amet nisl. Arcu odio ut sem nulla.
`;
+
+export const InputContainer = ({ children }) => {children}
;
diff --git a/yarn.lock b/yarn.lock
index 0122082..d2e539c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1896,6 +1896,11 @@ aproba@^1.0.3, aproba@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
+are-passive-events-supported@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/are-passive-events-supported/-/are-passive-events-supported-1.1.1.tgz#3db180a1753a2186a2de50a32cded3ac0979f5dc"
+ integrity sha512-5wnvlvB/dTbfrCvJ027Y4L4gW/6Mwoy1uFSavney0YO++GU+0e/flnjiBBwH+1kh7xNCgCOGvmJC3s32joYbww==
+
are-we-there-yet@~1.1.2:
version "1.1.5"
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21"
@@ -7461,6 +7466,19 @@ use-callback-ref@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.2.1.tgz#898759ccb9e14be6c7a860abafa3ffbd826c89bb"
+use-latest@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/use-latest/-/use-latest-1.0.0.tgz#c86d2e4893b15f27def69da574a47136d107facb"
+ integrity sha512-CxmFi75KTXeTIBlZq3LhJ4Hz98pCaRKZHCpnbiaEHIr5QnuHvH8lKYoluPBt/ik7j/hFVPB8K3WqF6mQvLyQTg==
+
+use-onclickoutside@^0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/use-onclickoutside/-/use-onclickoutside-0.3.1.tgz#fdd723a6a499046b6bc761e4a03af432eee5917b"
+ integrity sha512-aahvbW5+G0XJfzj31FJeLsvc6qdKbzeTsQ8EtkHHq5qTg6bm/qkJeKLcgrpnYeHDDbd7uyhImLGdkbM9BRzOHQ==
+ dependencies:
+ are-passive-events-supported "^1.1.0"
+ use-latest "^1.0.0"
+
use-sidecar@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.0.2.tgz#e72f582a75842f7de4ef8becd6235a4720ad8af6"