From 75da6b87b3ae9c7a14548ea1cd38b427e1e167c5 Mon Sep 17 00:00:00 2001 From: Kacper Krzywiec <49066275+kark@users.noreply.github.com> Date: Thu, 13 Apr 2023 13:03:12 +0200 Subject: [PATCH] docs: `menuPortalZIndex` docs improvements (#2484) * docs: make menuPortalZIndex docs more explicit * refactor: copy implementation to all stories * refactor: warn when menuPortalZIndex used without menuPortalTarget * chore: add changeset * refactor: change and externalize implementation of warning * refactor: dry * refactor: dry part 2 * chore: rename * chore: rename --- .changeset/strong-spoons-provide.md | 19 +++ docs/.storybook/decorators/index.js | 1 + .../neighbouring-stacking-context/index.js | 1 + .../neighbouring-stacking-context.js | 28 ++++ .../.storybook/utils/add-menu-portal-props.js | 21 +++ docs/.storybook/utils/index.js | 1 + .../async-creatable-select-field/README.md | 2 +- .../src/async-creatable-select-field.story.js | 143 +++++++++--------- .../src/async-creatable-select-field.tsx | 2 + .../fields/async-select-field/README.md | 2 +- .../src/async-select-field.story.js | 125 +++++++-------- .../src/async-select-field.tsx | 2 + .../fields/creatable-select-field/README.md | 2 +- .../src/creatable-select-field.story.js | 137 +++++++++-------- .../src/creatable-select-field.tsx | 2 + .../components/fields/money-field/README.md | 2 +- .../money-field/src/money-field.story.js | 115 +++++++------- .../fields/money-field/src/money-field.tsx | 2 + .../fields/search-select-field/README.md | 2 +- .../src/search-select-field.story.js | 9 +- .../src/search-select-field.tsx | 2 + .../components/fields/select-field/README.md | 2 +- .../select-field/src/select-field.story.js | 123 ++++++++------- .../fields/select-field/src/select-field.tsx | 3 + .../async-creatable-select-input/README.md | 2 +- .../src/async-creatable-select-input.story.js | 9 +- .../src/async-creatable-select-input.tsx | 9 ++ .../inputs/async-select-input/README.md | 2 +- .../src/async-select-input.story.js | 9 +- .../src/async-select-input.tsx | 9 ++ .../inputs/creatable-select-input/README.md | 2 +- .../src/creatable-select-input.story.js | 100 ++++++------ .../src/creatable-select-input.tsx | 2 + .../components/inputs/money-input/README.md | 2 +- .../money-input/src/money-input.story.js | 65 ++++---- .../inputs/money-input/src/money-input.tsx | 9 ++ .../inputs/search-select-input/README.md | 2 +- .../src/search-select-input.story.js | 9 +- .../src/search-select-input.tsx | 9 ++ .../components/inputs/select-input/README.md | 2 +- .../select-input/src/select-input.story.js | 88 ++++++----- .../inputs/select-input/src/select-input.tsx | 9 ++ .../inputs/select-utils/src/index.ts | 1 + .../inputs/select-utils/src/warning.ts | 22 +++ .../inputs/selectable-search-input/README.md | 2 +- .../src/selectable-search-input.story.js | 79 +++++----- .../src/selectable-search-input.tsx | 11 +- 47 files changed, 723 insertions(+), 479 deletions(-) create mode 100644 .changeset/strong-spoons-provide.md create mode 100644 docs/.storybook/decorators/neighbouring-stacking-context/index.js create mode 100644 docs/.storybook/decorators/neighbouring-stacking-context/neighbouring-stacking-context.js create mode 100644 docs/.storybook/utils/add-menu-portal-props.js create mode 100644 docs/.storybook/utils/index.js create mode 100644 packages/components/inputs/select-utils/src/warning.ts diff --git a/.changeset/strong-spoons-provide.md b/.changeset/strong-spoons-provide.md new file mode 100644 index 0000000000..867ad2415b --- /dev/null +++ b/.changeset/strong-spoons-provide.md @@ -0,0 +1,19 @@ +--- +'@commercetools-uikit/async-creatable-select-field': patch +'@commercetools-uikit/async-creatable-select-input': patch +'@commercetools-uikit/selectable-search-input': patch +'@commercetools-uikit/creatable-select-field': patch +'@commercetools-uikit/creatable-select-input': patch +'@commercetools-uikit/search-select-field': patch +'@commercetools-uikit/search-select-input': patch +'@commercetools-uikit/async-select-field': patch +'@commercetools-uikit/async-select-input': patch +'@commercetools-uikit/select-field': patch +'@commercetools-uikit/select-input': patch +'@commercetools-uikit/select-utils': patch +'@commercetools-uikit/money-field': patch +'@commercetools-uikit/money-input': patch +--- + +Improve docs regarding the use of `menuPortalZIndex` prop. +Warn when `menuPortalZIndex` is used without setting `menuPortalTarget`. diff --git a/docs/.storybook/decorators/index.js b/docs/.storybook/decorators/index.js index 8031801148..c2fb73f8b4 100644 --- a/docs/.storybook/decorators/index.js +++ b/docs/.storybook/decorators/index.js @@ -1,2 +1,3 @@ export { default as FormikBox } from './formik-box'; export { default as Section } from './section'; +export { default as NeighbouringStackingContext } from './neighbouring-stacking-context'; diff --git a/docs/.storybook/decorators/neighbouring-stacking-context/index.js b/docs/.storybook/decorators/neighbouring-stacking-context/index.js new file mode 100644 index 0000000000..0cc224d1db --- /dev/null +++ b/docs/.storybook/decorators/neighbouring-stacking-context/index.js @@ -0,0 +1 @@ +export { default } from './neighbouring-stacking-context'; diff --git a/docs/.storybook/decorators/neighbouring-stacking-context/neighbouring-stacking-context.js b/docs/.storybook/decorators/neighbouring-stacking-context/neighbouring-stacking-context.js new file mode 100644 index 0000000000..645a6371b3 --- /dev/null +++ b/docs/.storybook/decorators/neighbouring-stacking-context/neighbouring-stacking-context.js @@ -0,0 +1,28 @@ +import Card from '@commercetools-uikit/card'; +import styled from '@emotion/styled'; +import { boolean } from '@storybook/addon-knobs/react'; + +const StackingContext = styled.div` + width: 300px; + height: 50px; + position: relative; + z-index: 2; +`; + +const NeighbouringStackingContext = () => { + const isActive = boolean( + 'menuPortalZIndex-show-neighbouring-stacking-context', + false + ); + return ( + isActive && ( + + + Stacking context with z-index: 2 + + + ) + ); +}; + +export default NeighbouringStackingContext; diff --git a/docs/.storybook/utils/add-menu-portal-props.js b/docs/.storybook/utils/add-menu-portal-props.js new file mode 100644 index 0000000000..4e9d5948e8 --- /dev/null +++ b/docs/.storybook/utils/add-menu-portal-props.js @@ -0,0 +1,21 @@ +import { select } from '@storybook/addon-knobs/react'; + +const getMenuPortalTargetValue = (menuPortalTarget) => { + if (menuPortalTarget === 'document.body') { + return document.body; + } + return undefined; +}; + +export const addMenuPortalProps = () => { + const menuPortalZIndex = select('menuPortalZIndex', [1, 2, 3], 1); + const menuPortalTarget = select( + 'menuPortalTarget', + ['undefined', 'document.body'], + 'undefined' + ); + return { + menuPortalZIndex, + menuPortalTarget: getMenuPortalTargetValue(menuPortalTarget), + }; +}; diff --git a/docs/.storybook/utils/index.js b/docs/.storybook/utils/index.js new file mode 100644 index 0000000000..f440c3ad99 --- /dev/null +++ b/docs/.storybook/utils/index.js @@ -0,0 +1 @@ +export { addMenuPortalProps } from './add-menu-portal-props'; diff --git a/packages/components/fields/async-creatable-select-field/README.md b/packages/components/fields/async-creatable-select-field/README.md index 358b3ec73a..4be3ba021e 100644 --- a/packages/components/fields/async-creatable-select-field/README.md +++ b/packages/components/fields/async-creatable-select-field/README.md @@ -76,7 +76,7 @@ export default Example; | `hasWarning` | `boolean` | | | Indicates the input field has a warning | | `maxMenuHeight` | `AsyncCreatableProps['maxMenuHeight']` | | | Maximum height of the menu before scrolling
[Props from React select was used](https://react-select.com/props) | | `menuPortalTarget` | `AsyncCreatableProps['menuPortalTarget']` | | | Dom element to portal the select menu to
[Props from React select was used](https://react-select.com/props) | -| `menuPortalZIndex` | `number` | | | z-index value for the menu portal | +| `menuPortalZIndex` | `number` | | | z-index value for the menu portal
Use in conjunction with `menuPortalTarget` | | `menuShouldBlockScroll` | `AsyncCreatableProps['menuShouldBlockScroll']` | | | whether the menu should block scroll while open
[Props from React select was used](https://react-select.com/props) | | `name` | `AsyncCreatableProps['name']` | | | Name of the HTML Input (optional - without this, no input will be rendered)
[Props from React select was used](https://react-select.com/props) | | `noOptionsMessage` | `AsyncCreatableProps['noOptionsMessage']` | | | Can be used to render a custom value when there are no options (either because of no search results, or all options have been used, or there were none in the first place).
Gets called with `{ inputValue: String }`. `inputValue` will be an empty string when no search text is present.
[Props from React select was used](https://react-select.com/props) | diff --git a/packages/components/fields/async-creatable-select-field/src/async-creatable-select-field.story.js b/packages/components/fields/async-creatable-select-field/src/async-creatable-select-field.story.js index ae325e1b87..104f6add9c 100644 --- a/packages/components/fields/async-creatable-select-field/src/async-creatable-select-field.story.js +++ b/packages/components/fields/async-creatable-select-field/src/async-creatable-select-field.story.js @@ -11,7 +11,10 @@ import { number, } from '@storybook/addon-knobs/react'; import Constraints from '@commercetools-uikit/constraints'; +import Spacings from '@commercetools-uikit/spacings'; import Section from '../../../../../docs/.storybook/decorators/section'; +import NeighbouringStackingContext from '../../../../../docs/.storybook/decorators/neighbouring-stacking-context'; +import { addMenuPortalProps } from '../../../../../docs/.storybook/utils'; import * as icons from '../../../icons'; import Readme from '../README.md'; import AsyncCreatableSelectField from './async-creatable-select-field'; @@ -109,75 +112,79 @@ storiesOf('Components|Fields/SelectFields', module) key={isMulti} defaultValue={isMulti ? [] : undefined} render={(value, onChange) => ( - { - switch (key) { - case 'customError': - return 'A custom error.'; - default: - return null; + + { + switch (key) { + case 'customError': + return 'A custom error.'; + default: + return null; + } + }} + isRequired={boolean('isRequired', false)} + touched={boolean('touched', false)} + aria-label={text('aria-label', '')} + aria-labelledby={text('aria-labelledby', '')} + backspaceRemovesValue={boolean('backspaceRemovesValue', true)} + containerId={text('containerId', '')} + id={id || undefined} + name={name} + value={value} + onChange={(event) => { + action('onChange')(event); + onChange(event.target.value); + }} + onBlur={action('onBlur')} + onFocus={action('onFocus')} + onInputChange={action('onInputChange')} + isAutofocussed={boolean('isAutofocussed', false)} + isDisabled={boolean('isDisabled', false)} + isReadOnly={boolean('isReadOnly', false)} + isMulti={isMulti} + hasWarning={boolean('hasWarning', false)} + placeholder={text('placeholder', 'Select...')} + title={text('title', 'Favourite animal')} + maxMenuHeight={number('maxMenuHeight', 220)} + isSearchable={boolean('isSearchable', false)} + isClearable={boolean('isClearable', false)} + tabIndex={text('tabIndex', '0')} + tabSelectsValue={boolean('tabSelectsValue', true)} + // Async props + defaultOptions={defaultOptions} + loadOptions={loadOptions} + cacheOptions={boolean('cacheOptions', false)} + // Creatable props + allowCreateWhileLoading={boolean( + 'allowCreateWhileLoading', + false + )} + createOptionPosition={select( + 'createOptionPosition', + ['first', 'last'], + 'last' + )} + // FieldLabel + hint={hint} + description={text('description', '')} + onInfoButtonClick={ + boolean('show info button', false) + ? action('onInfoButtonClick') + : undefined } - }} - isRequired={boolean('isRequired', false)} - touched={boolean('touched', false)} - aria-label={text('aria-label', '')} - aria-labelledby={text('aria-labelledby', '')} - backspaceRemovesValue={boolean('backspaceRemovesValue', true)} - containerId={text('containerId', '')} - id={id || undefined} - name={name} - value={value} - onChange={(event) => { - action('onChange')(event); - onChange(event.target.value); - }} - onBlur={action('onBlur')} - onFocus={action('onFocus')} - onInputChange={action('onInputChange')} - isAutofocussed={boolean('isAutofocussed', false)} - isDisabled={boolean('isDisabled', false)} - isReadOnly={boolean('isReadOnly', false)} - isMulti={isMulti} - hasWarning={boolean('hasWarning', false)} - placeholder={text('placeholder', 'Select...')} - title={text('title', 'Favourite animal')} - maxMenuHeight={number('maxMenuHeight', 220)} - isSearchable={boolean('isSearchable', false)} - isClearable={boolean('isClearable', false)} - tabIndex={text('tabIndex', '0')} - tabSelectsValue={boolean('tabSelectsValue', true)} - // Async props - defaultOptions={defaultOptions} - loadOptions={loadOptions} - cacheOptions={boolean('cacheOptions', false)} - // Creatable props - allowCreateWhileLoading={boolean( - 'allowCreateWhileLoading', - false - )} - createOptionPosition={select( - 'createOptionPosition', - ['first', 'last'], - 'last' - )} - // FieldLabel - hint={hint} - description={text('description', '')} - onInfoButtonClick={ - boolean('show info button', false) - ? action('onInfoButtonClick') - : undefined - } - hintIcon={hintIcon} - badge={text('badge', '')} - iconLeft={iconLeft ? createElement(iconLeft) : undefined} - /> + hintIcon={hintIcon} + badge={text('badge', '')} + iconLeft={iconLeft ? createElement(iconLeft) : undefined} + {...addMenuPortalProps()} + /> + + )} /> diff --git a/packages/components/fields/async-creatable-select-field/src/async-creatable-select-field.tsx b/packages/components/fields/async-creatable-select-field/src/async-creatable-select-field.tsx index e4d055f8fd..ac2fbc3b19 100644 --- a/packages/components/fields/async-creatable-select-field/src/async-creatable-select-field.tsx +++ b/packages/components/fields/async-creatable-select-field/src/async-creatable-select-field.tsx @@ -187,6 +187,8 @@ export type TAsyncCreatableSelectFieldProps = { menuPortalTarget?: ReactSelectAsyncCreatableProps['menuPortalTarget']; /** * z-index value for the menu portal + *
+ * Use in conjunction with `menuPortalTarget` */ menuPortalZIndex?: number; /** diff --git a/packages/components/fields/async-select-field/README.md b/packages/components/fields/async-select-field/README.md index d9785d06f2..b01473fcf3 100644 --- a/packages/components/fields/async-select-field/README.md +++ b/packages/components/fields/async-select-field/README.md @@ -74,7 +74,7 @@ export default Example; | `hasWarning` | `boolean` | | | Indicates the input field has a warning | | `maxMenuHeight` | `AsyncProps['maxMenuHeight']` | | | Maximum height of the menu before scrolling
[Props from React select was used](https://react-select.com/props) | | `menuPortalTarget` | `AsyncProps['menuPortalTarget']` | | | Dom element to portal the select menu to
[Props from React select was used](https://react-select.com/props) | -| `menuPortalZIndex` | `number` | | | z-index value for the menu portal | +| `menuPortalZIndex` | `number` | | | z-index value for the menu portal
Use in conjunction with `menuPortalTarget` | | `menuShouldBlockScroll` | `AsyncProps['menuShouldBlockScroll']` | | | whether the menu should block scroll while open
[Props from React select was used](https://react-select.com/props) | | `name` | `AsyncProps['name']` | | | Name of the HTML Input (optional - without this, no input will be rendered)
[Props from React select was used](https://react-select.com/props) | | `noOptionsMessage` | `AsyncProps['noOptionsMessage']` | | | Can be used to render a custom value when there are no options (either because of no search results, or all options have been used, or there were none in the first place).
[Props from React select was used](https://react-select.com/props) | diff --git a/packages/components/fields/async-select-field/src/async-select-field.story.js b/packages/components/fields/async-select-field/src/async-select-field.story.js index d59343ca39..238039104d 100644 --- a/packages/components/fields/async-select-field/src/async-select-field.story.js +++ b/packages/components/fields/async-select-field/src/async-select-field.story.js @@ -11,7 +11,10 @@ import { number, } from '@storybook/addon-knobs/react'; import Constraints from '@commercetools-uikit/constraints'; +import Spacings from '@commercetools-uikit/spacings'; import Section from '../../../../../docs/.storybook/decorators/section'; +import NeighbouringStackingContext from '../../../../../docs/.storybook/decorators/neighbouring-stacking-context'; +import { addMenuPortalProps } from '../../../../../docs/.storybook/utils'; import Readme from '../README.md'; import * as icons from '../../../icons'; import AsyncSelectField from './async-select-field'; @@ -110,66 +113,70 @@ storiesOf('Components|Fields/SelectFields', module) key={isMulti} defaultValue={isMulti ? [] : undefined} render={(value, onChange) => ( - { - switch (key) { - case 'customError': - return 'A custom error.'; - default: - return null; + + { + switch (key) { + case 'customError': + return 'A custom error.'; + default: + return null; + } + }} + isRequired={boolean('isRequired', false)} + touched={boolean('touched', false)} + aria-label={text('aria-label', '')} + aria-labelledby={text('aria-labelledby', '')} + backspaceRemovesValue={boolean('backspaceRemovesValue', true)} + containerId={text('containerId', '')} + id={id || undefined} + name={name} + value={value} + onChange={(event) => { + action('onChange')(event); + onChange(event.target.value); + }} + onBlur={action('onBlur')} + onFocus={action('onFocus')} + onInputChange={action('onInputChange')} + isAutofocussed={boolean('isAutofocussed', false)} + isDisabled={boolean('isDisabled', false)} + isReadOnly={boolean('isReadOnly', false)} + isMulti={isMulti} + hasWarning={boolean('hasWarning', false)} + placeholder={text('placeholder', 'Select...')} + loadingMessage={loadingMessage} + title={text('title', 'Favourite animal')} + maxMenuHeight={number('maxMenuHeight', 220)} + isSearchable={boolean('isSearchable', true)} + isClearable={boolean('isClearable', false)} + tabIndex={text('tabIndex', '0')} + tabSelectsValue={boolean('tabSelectsValue', true)} + // Async props + defaultOptions={defaultOptions} + loadOptions={loadOptions} + cacheOptions={boolean('cacheOptions', false)} + // FieldLabel + hint={hint} + description={text('description', '')} + onInfoButtonClick={ + boolean('show info button', false) + ? action('onInfoButtonClick') + : undefined } - }} - isRequired={boolean('isRequired', false)} - touched={boolean('touched', false)} - aria-label={text('aria-label', '')} - aria-labelledby={text('aria-labelledby', '')} - backspaceRemovesValue={boolean('backspaceRemovesValue', true)} - containerId={text('containerId', '')} - id={id || undefined} - name={name} - value={value} - onChange={(event) => { - action('onChange')(event); - onChange(event.target.value); - }} - onBlur={action('onBlur')} - onFocus={action('onFocus')} - onInputChange={action('onInputChange')} - isAutofocussed={boolean('isAutofocussed', false)} - isDisabled={boolean('isDisabled', false)} - isReadOnly={boolean('isReadOnly', false)} - isMulti={isMulti} - hasWarning={boolean('hasWarning', false)} - placeholder={text('placeholder', 'Select...')} - loadingMessage={loadingMessage} - title={text('title', 'Favourite animal')} - maxMenuHeight={number('maxMenuHeight', 220)} - isSearchable={boolean('isSearchable', true)} - isClearable={boolean('isClearable', false)} - tabIndex={text('tabIndex', '0')} - tabSelectsValue={boolean('tabSelectsValue', true)} - // Async props - defaultOptions={defaultOptions} - loadOptions={loadOptions} - cacheOptions={boolean('cacheOptions', false)} - // FieldLabel - hint={hint} - description={text('description', '')} - onInfoButtonClick={ - boolean('show info button', false) - ? action('onInfoButtonClick') - : undefined - } - hintIcon={hintIcon} - badge={text('badge', '')} - iconLeft={iconLeft ? createElement(iconLeft) : undefined} - /> + hintIcon={hintIcon} + badge={text('badge', '')} + iconLeft={iconLeft ? createElement(iconLeft) : undefined} + {...addMenuPortalProps()} + /> + + )} /> diff --git a/packages/components/fields/async-select-field/src/async-select-field.tsx b/packages/components/fields/async-select-field/src/async-select-field.tsx index 883375ed5e..5ca66e9c6f 100644 --- a/packages/components/fields/async-select-field/src/async-select-field.tsx +++ b/packages/components/fields/async-select-field/src/async-select-field.tsx @@ -182,6 +182,8 @@ export type TAsyncSelectFieldProps = { menuPortalTarget?: ReactSelectAsyncProps['menuPortalTarget']; /** * z-index value for the menu portal + *
+ * Use in conjunction with `menuPortalTarget` */ menuPortalZIndex?: number; /** diff --git a/packages/components/fields/creatable-select-field/README.md b/packages/components/fields/creatable-select-field/README.md index 34d5287492..3405139f48 100644 --- a/packages/components/fields/creatable-select-field/README.md +++ b/packages/components/fields/creatable-select-field/README.md @@ -73,7 +73,7 @@ export default Example; | `hasWarning` | `boolean` | | | Indicates the input field has an error | | `maxMenuHeight` | `CreatableProps['maxMenuHeight']` | | | Maximum height of the menu before scrolling
[Props from React select was used](https://react-select.com/props#creatable-props) | | `menuPortalTarget` | `CreatableProps['menuPortalTarget']` | | | Dom element to portal the select menu to
[Props from React select was used](https://react-select.com/props#creatable-props) | -| `menuPortalZIndex` | `number` | | | z-index value for the menu portal | +| `menuPortalZIndex` | `number` | | | z-index value for the menu portal
Use in conjunction with `menuPortalTarget` | | `menuShouldBlockScroll` | `CreatableProps['menuShouldBlockScroll']` | | | whether the menu should block scroll while open
[Props from React select was used](https://react-select.com/props#creatable-props) | | `name` | `CreatableProps['name']` | | | Name of the HTML Input (optional - without this, no input will be rendered)
[Props from React select was used](https://react-select.com/props#creatable-props) | | `noOptionsMessage` | `CreatableProps['noOptionsMessage']` | | | Can be used to render a custom value when there are no options (either because of no search results, or all options have been used, or there were none in the first place).
Gets called with `{ inputValue: String }`. `inputValue` will be an empty string when no search text is present.
[Props from React select was used](https://react-select.com/props#creatable-props) | diff --git a/packages/components/fields/creatable-select-field/src/creatable-select-field.story.js b/packages/components/fields/creatable-select-field/src/creatable-select-field.story.js index 95788ad8ca..d67f0a5093 100644 --- a/packages/components/fields/creatable-select-field/src/creatable-select-field.story.js +++ b/packages/components/fields/creatable-select-field/src/creatable-select-field.story.js @@ -11,7 +11,10 @@ import { number, } from '@storybook/addon-knobs/react'; import Constraints from '@commercetools-uikit/constraints'; +import Spacings from '@commercetools-uikit/spacings'; import Section from '../../../../../docs/.storybook/decorators/section'; +import NeighbouringStackingContext from '../../../../../docs/.storybook/decorators/neighbouring-stacking-context'; +import { addMenuPortalProps } from '../../../../../docs/.storybook/utils'; import Readme from '../README.md'; import * as icons from '../../../icons'; import CreatableSelectField from './creatable-select-field'; @@ -95,72 +98,76 @@ storiesOf('Components|Fields/SelectFields', module) key={isMulti} defaultValue={isMulti ? [] : undefined} render={(value, onChange) => ( - { - switch (key) { - case 'customError': - return 'A custom error.'; - default: - return null; + + { + switch (key) { + case 'customError': + return 'A custom error.'; + default: + return null; + } + }} + isRequired={boolean('isRequired', false)} + touched={boolean('touched', false)} + aria-label={text('aria-label', '')} + aria-labelledby={text('aria-labelledby', '')} + backspaceRemovesValue={boolean('backspaceRemovesValue', true)} + containerId={text('containerId', '')} + id={id || undefined} + name={name} + value={value} + onChange={(event) => { + action('onChange')(event); + onChange(event.target.value); + }} + onBlur={action('onBlur')} + onFocus={action('onFocus')} + onInputChange={action('onInputChange')} + isAutofocussed={boolean('isAutofocussed', false)} + isDisabled={boolean('isDisabled', false)} + isReadOnly={boolean('isReadOnly', false)} + isMulti={isMulti} + hasWarning={boolean('hasWarning', false)} + placeholder={text('placeholder', 'Select...')} + title={text('title', 'Favourite animal')} + maxMenuHeight={number('maxMenuHeight', 220)} + isSearchable={boolean('isSearchable', true)} + isClearable={boolean('isClearable', false)} + options={options} + tabIndex={text('tabIndex', '0')} + tabSelectsValue={boolean('tabSelectsValue', true)} + // Creatable props + allowCreateWhileLoading={boolean( + 'allowCreateWhileLoading', + false + )} + createOptionPosition={select( + 'createOptionPosition', + ['first', 'last'], + 'last' + )} + // FieldLabel props + hint={hint} + description={text('description', '')} + onInfoButtonClick={ + boolean('show info button', false) + ? action('onInfoButtonClick') + : undefined } - }} - isRequired={boolean('isRequired', false)} - touched={boolean('touched', false)} - aria-label={text('aria-label', '')} - aria-labelledby={text('aria-labelledby', '')} - backspaceRemovesValue={boolean('backspaceRemovesValue', true)} - containerId={text('containerId', '')} - id={id || undefined} - name={name} - value={value} - onChange={(event) => { - action('onChange')(event); - onChange(event.target.value); - }} - onBlur={action('onBlur')} - onFocus={action('onFocus')} - onInputChange={action('onInputChange')} - isAutofocussed={boolean('isAutofocussed', false)} - isDisabled={boolean('isDisabled', false)} - isReadOnly={boolean('isReadOnly', false)} - isMulti={isMulti} - hasWarning={boolean('hasWarning', false)} - placeholder={text('placeholder', 'Select...')} - title={text('title', 'Favourite animal')} - maxMenuHeight={number('maxMenuHeight', 220)} - isSearchable={boolean('isSearchable', true)} - isClearable={boolean('isClearable', false)} - options={options} - tabIndex={text('tabIndex', '0')} - tabSelectsValue={boolean('tabSelectsValue', true)} - // Creatable props - allowCreateWhileLoading={boolean( - 'allowCreateWhileLoading', - false - )} - createOptionPosition={select( - 'createOptionPosition', - ['first', 'last'], - 'last' - )} - // FieldLabel props - hint={hint} - description={text('description', '')} - onInfoButtonClick={ - boolean('show info button', false) - ? action('onInfoButtonClick') - : undefined - } - hintIcon={hintIcon} - badge={text('badge', '')} - iconLeft={iconLeft ? createElement(iconLeft) : undefined} - /> + hintIcon={hintIcon} + badge={text('badge', '')} + iconLeft={iconLeft ? createElement(iconLeft) : undefined} + {...addMenuPortalProps()} + /> + + )} /> diff --git a/packages/components/fields/creatable-select-field/src/creatable-select-field.tsx b/packages/components/fields/creatable-select-field/src/creatable-select-field.tsx index 13c2d15730..ba329bc542 100644 --- a/packages/components/fields/creatable-select-field/src/creatable-select-field.tsx +++ b/packages/components/fields/creatable-select-field/src/creatable-select-field.tsx @@ -192,6 +192,8 @@ export type TCreatableSelectFieldProps = { menuPortalTarget?: ReactSelectCreatableProps['menuPortalTarget']; /** * z-index value for the menu portal + *
+ * Use in conjunction with `menuPortalTarget` */ menuPortalZIndex?: number; /** diff --git a/packages/components/fields/money-field/README.md b/packages/components/fields/money-field/README.md index 2cca96170b..d92efaee28 100644 --- a/packages/components/fields/money-field/README.md +++ b/packages/components/fields/money-field/README.md @@ -79,7 +79,7 @@ export default Example; | `isAutofocussed` | `boolean` | | | Focus the input on initial render | | `onChange` | `Function`
[See signature.](#signature-onChange) | | | Called with the event of the input or dropdown when either the currency or the amount have changed. | | `menuPortalTarget` | `ReactSelectProps['menuPortalTarget']` | | | Dom element to portal the currency select menu to
[Props from React select was used](https://react-select.com/props) | -| `menuPortalZIndex` | `number` | | | z-index value for the currency select menu portal | +| `menuPortalZIndex` | `number` | | | z-index value for the currency select menu portal
Use in conjunction with `menuPortalTarget` | | `menuShouldBlockScroll` | `ReactSelectProps['menuShouldBlockScroll']` | | | whether the menu should block scroll while open
[Props from React select was used](https://react-select.com/props) | | `title` | `union`
Possible values:
`string , ReactNode` | ✅ | | Title of the label | | `hint` | `union`
Possible values:
`string , ReactNode` | | | Hint for the label. Provides a supplementary but important information regarding the behaviour of the input (e.g warn about uniqueness of a field, when it can only be set once), whereas `description` can describe it in more depth. Can also receive a `hintIcon`. | diff --git a/packages/components/fields/money-field/src/money-field.story.js b/packages/components/fields/money-field/src/money-field.story.js index 4858bd8d32..ccfbbd370a 100644 --- a/packages/components/fields/money-field/src/money-field.story.js +++ b/packages/components/fields/money-field/src/money-field.story.js @@ -9,8 +9,11 @@ import { object, } from '@storybook/addon-knobs/react'; import Constraints from '@commercetools-uikit/constraints'; +import Spacings from '@commercetools-uikit/spacings'; import { injectIntl } from 'react-intl'; import Section from '../../../../../docs/.storybook/decorators/section'; +import NeighbouringStackingContext from '../../../../../docs/.storybook/decorators/neighbouring-stacking-context'; +import { addMenuPortalProps } from '../../../../../docs/.storybook/utils'; import Readme from '../README.md'; import * as icons from '../../../icons'; import MoneyField from './money-field'; @@ -60,64 +63,68 @@ class MoneyFieldStory extends Component { const hintIcon = icon ? createElement(icons[icon]) : undefined; return (
- { - switch (key) { - case 'customError': - return 'A custom error.'; - default: - return null; + + { + switch (key) { + case 'customError': + return 'A custom error.'; + default: + return null; + } + }} + isRequired={boolean('isRequired', false)} + touched={ + boolean('touched', false) + ? { amount: true, currencyCode: true } + : { amount: false, currencyCode: false } } - }} - isRequired={boolean('isRequired', false)} - touched={ - boolean('touched', false) - ? { amount: true, currencyCode: true } - : { amount: false, currencyCode: false } - } - // MoneyInput - name={name} - value={{ - amount: this.state.amount, - currencyCode: this.state.currencyCode, - }} - currencies={boolean('dropdown', true) ? currencies : undefined} - placeholder={text('placeholder', 'Placeholder')} - onBlur={action('onBlur')} - isDisabled={boolean('isDisabled', false)} - isReadOnly={boolean('isReadOnly', false)} - isAutofocussed={boolean('isAutofocussed', false)} - onChange={(event) => { - action('onChange')(event); + // MoneyInput + name={name} + value={{ + amount: this.state.amount, + currencyCode: this.state.currencyCode, + }} + currencies={boolean('dropdown', true) ? currencies : undefined} + placeholder={text('placeholder', 'Placeholder')} + onBlur={action('onBlur')} + isDisabled={boolean('isDisabled', false)} + isReadOnly={boolean('isReadOnly', false)} + isAutofocussed={boolean('isAutofocussed', false)} + onChange={(event) => { + action('onChange')(event); - if (event.target.name.endsWith('.amount')) { - this.setState({ amount: event.target.value }); - } + if (event.target.name.endsWith('.amount')) { + this.setState({ amount: event.target.value }); + } - if (event.target.name.endsWith('.currencyCode')) { - this.setState({ currencyCode: event.target.value }); + if (event.target.name.endsWith('.currencyCode')) { + this.setState({ currencyCode: event.target.value }); + } + }} + // LabelField + title={text('title', 'Price')} + hint={hint} + description={text('description', '')} + onInfoButtonClick={ + boolean('show info button', false) + ? action('onInfoButtonClick') + : undefined } - }} - // LabelField - title={text('title', 'Price')} - hint={hint} - description={text('description', '')} - onInfoButtonClick={ - boolean('show info button', false) - ? action('onInfoButtonClick') - : undefined - } - hintIcon={hintIcon} - hasHighPrecisionBadge={boolean('hasHighPrecisionBadge', false)} - /> + hintIcon={hintIcon} + hasHighPrecisionBadge={boolean('hasHighPrecisionBadge', false)} + {...addMenuPortalProps()} + /> + +
); } diff --git a/packages/components/fields/money-field/src/money-field.tsx b/packages/components/fields/money-field/src/money-field.tsx index e59a1c047a..3f537f2108 100644 --- a/packages/components/fields/money-field/src/money-field.tsx +++ b/packages/components/fields/money-field/src/money-field.tsx @@ -157,6 +157,8 @@ export type TMoneyFieldProps = { menuPortalTarget?: ReactSelectProps['menuPortalTarget']; /** * z-index value for the currency select menu portal + *
+ * Use in conjunction with `menuPortalTarget` */ menuPortalZIndex?: number; /** diff --git a/packages/components/fields/search-select-field/README.md b/packages/components/fields/search-select-field/README.md index 0a4d7c1c5d..7c1dd1f88e 100644 --- a/packages/components/fields/search-select-field/README.md +++ b/packages/components/fields/search-select-field/README.md @@ -107,7 +107,7 @@ export default Example; | `noOptionsMessage` | `AsyncProps['noOptionsMessage']` | | | Can be used to render a custom value when there are no options (either because of no search results, or all options have been used, or there were none in the first place). Gets called with `{ inputValue: String }`. `inputValue` will be an empty string when no search text is present.
[Props from React select was used](https://react-select.com/props) | | `maxMenuHeight` | `AsyncProps['maxMenuHeight']` | | | Maximum height of the menu before scrolling
[Props from React select was used](https://react-select.com/props) | | `menuPortalTarget` | `AsyncProps['menuPortalTarget']` | | | Dom element to portal the select menu to
[Props from React select was used](https://react-select.com/props) | -| `menuPortalZIndex` | `number` | | | z-index value for the menu portal | +| `menuPortalZIndex` | `number` | | | z-index value for the menu portal
Use in conjunction with `menuPortalTarget` | | `menuShouldBlockScroll` | `boolean` | | | whether the menu should block scroll while open | | `showOptionGroupDivider` | `boolean` | | | Determines if option groups will be separated by a divider | | `onBlur` | `Function`
[See signature.](#signature-onBlur) | | | Handle blur events on the control | diff --git a/packages/components/fields/search-select-field/src/search-select-field.story.js b/packages/components/fields/search-select-field/src/search-select-field.story.js index 8229c1eaca..17b616b67d 100644 --- a/packages/components/fields/search-select-field/src/search-select-field.story.js +++ b/packages/components/fields/search-select-field/src/search-select-field.story.js @@ -11,8 +11,11 @@ import { number, } from '@storybook/addon-knobs/react'; import Constraints from '@commercetools-uikit/constraints'; +import Spacings from '@commercetools-uikit/spacings'; import { SELECT_DROPDOWN_OPTION_TYPES } from '../../../inputs/select-utils'; import Section from '../../../../../docs/.storybook/decorators/section'; +import NeighbouringStackingContext from '../../../../../docs/.storybook/decorators/neighbouring-stacking-context'; +import { addMenuPortalProps } from '../../../../../docs/.storybook/utils'; import Readme from '../README.md'; import * as icons from '../../../icons'; import SearchSelectField from './search-select-field'; @@ -83,7 +86,7 @@ storiesOf('Components|Fields/SelectFields', module) key={isMulti} defaultValue={isMulti ? [] : undefined} render={(value, onChange) => ( - <> + +

In this example, our `loadOptions` function uses the data @@ -161,7 +166,7 @@ storiesOf('Components|Fields/SelectFields', module) Data used:

{JSON.stringify(colourOptions, undefined, 2)}
- +
)} /> diff --git a/packages/components/fields/search-select-field/src/search-select-field.tsx b/packages/components/fields/search-select-field/src/search-select-field.tsx index 70d3da93c1..0f827beb54 100644 --- a/packages/components/fields/search-select-field/src/search-select-field.tsx +++ b/packages/components/fields/search-select-field/src/search-select-field.tsx @@ -179,6 +179,8 @@ export type TSearchSelectFieldProps = { menuPortalTarget?: ReactSelectAsyncProps['menuPortalTarget']; /** * z-index value for the menu portal + *
+ * Use in conjunction with `menuPortalTarget` */ menuPortalZIndex?: number; /** diff --git a/packages/components/fields/select-field/README.md b/packages/components/fields/select-field/README.md index bd35f38591..fffb097276 100644 --- a/packages/components/fields/select-field/README.md +++ b/packages/components/fields/select-field/README.md @@ -73,7 +73,7 @@ export default Example; | `isSearchable` | `boolean` | | | Whether to enable search functionality | | `maxMenuHeight` | `number` | | | Maximum height of the menu before scrolling | | `menuPortalTarget` | `ReactSelectProps['menuPortalTarget']` | | | Dom element to portal the select menu to
[Props from React select was used](https://react-select.com/props) | -| `menuPortalZIndex` | `number` | | | z-index value for the menu portal | +| `menuPortalZIndex` | `number` | | | z-index value for the menu portal
Use in conjunction with `menuPortalTarget` | | `menuShouldBlockScroll` | `boolean` | | | whether the menu should block scroll while open | | `name` | `string` | | | Name of the HTML Input (optional - without this, no input will be rendered) | | `noOptionsMessage` | `ReactSelectProps['noOptionsMessage']` | | | Can be used to render a custom value when there are no options (either because of no search results, or all options have been used, or there were none in the first place). Gets called with { inputValue: String }.
`inputValue` will be an empty string when no search text is present.
[Props from React select was used](https://react-select.com/props) | diff --git a/packages/components/fields/select-field/src/select-field.story.js b/packages/components/fields/select-field/src/select-field.story.js index 12927fa459..bdb0a2a21c 100644 --- a/packages/components/fields/select-field/src/select-field.story.js +++ b/packages/components/fields/select-field/src/select-field.story.js @@ -11,7 +11,10 @@ import { number, } from '@storybook/addon-knobs/react'; import Constraints from '@commercetools-uikit/constraints'; +import Spacings from '@commercetools-uikit/spacings'; import Section from '../../../../../docs/.storybook/decorators/section'; +import NeighbouringStackingContext from '../../../../../docs/.storybook/decorators/neighbouring-stacking-context'; +import { addMenuPortalProps } from '../../../../../docs/.storybook/utils'; import Readme from '../README.md'; import * as icons from '../../../icons'; import SelectField from './select-field'; @@ -95,65 +98,69 @@ storiesOf('Components|Fields/SelectFields', module) key={isMulti} defaultValue={isMulti ? [] : undefined} render={(value, onChange) => ( - { - switch (key) { - case 'customError': - return 'A custom error.'; - default: - return null; + + { + switch (key) { + case 'customError': + return 'A custom error.'; + default: + return null; + } + }} + isRequired={boolean('isRequired', false)} + touched={boolean('touched', false)} + aria-label={text('aria-label', '')} + aria-labelledby={text('aria-labelledby', '')} + backspaceRemovesValue={boolean('backspaceRemovesValue', true)} + controlShouldRenderValue={boolean( + 'controlShouldRenderValue', + true + )} + containerId={text('containerId', '')} + id={id || undefined} + name={name} + value={value} + onChange={(event) => { + action('onChange')(event); + onChange(event.target.value); + }} + onBlur={action('onBlur')} + onFocus={action('onFocus')} + onInputChange={action('onInputChange')} + isAutofocussed={boolean('isAutofocussed', false)} + isDisabled={boolean('isDisabled', false)} + isReadOnly={boolean('isReadOnly', false)} + isMulti={isMulti} + placeholder={text('placeholder', 'Select...')} + title={text('title', 'Favourite animal')} + maxMenuHeight={number('maxMenuHeight', 220)} + isSearchable={boolean('isSearchable', false)} + isClearable={boolean('isClearable', false)} + options={options} + tabIndex={text('tabIndex', '0')} + tabSelectsValue={boolean('tabSelectsValue', true)} + hint={hint} + description={text('description', '')} + onInfoButtonClick={ + boolean('show info button', false) + ? action('onInfoButtonClick') + : undefined } - }} - isRequired={boolean('isRequired', false)} - touched={boolean('touched', false)} - aria-label={text('aria-label', '')} - aria-labelledby={text('aria-labelledby', '')} - backspaceRemovesValue={boolean('backspaceRemovesValue', true)} - controlShouldRenderValue={boolean( - 'controlShouldRenderValue', - true - )} - containerId={text('containerId', '')} - id={id || undefined} - name={name} - value={value} - onChange={(event) => { - action('onChange')(event); - onChange(event.target.value); - }} - onBlur={action('onBlur')} - onFocus={action('onFocus')} - onInputChange={action('onInputChange')} - isAutofocussed={boolean('isAutofocussed', false)} - isDisabled={boolean('isDisabled', false)} - isReadOnly={boolean('isReadOnly', false)} - isMulti={isMulti} - placeholder={text('placeholder', 'Select...')} - title={text('title', 'Favourite animal')} - maxMenuHeight={number('maxMenuHeight', 220)} - isSearchable={boolean('isSearchable', false)} - isClearable={boolean('isClearable', false)} - options={options} - tabIndex={text('tabIndex', '0')} - tabSelectsValue={boolean('tabSelectsValue', true)} - hint={hint} - description={text('description', '')} - onInfoButtonClick={ - boolean('show info button', false) - ? action('onInfoButtonClick') - : undefined - } - hasWarning={boolean('hasWarning', false)} - hintIcon={hintIcon} - badge={text('badge', '')} - iconLeft={iconLeft ? createElement(iconLeft) : undefined} - /> + hasWarning={boolean('hasWarning', false)} + hintIcon={hintIcon} + badge={text('badge', '')} + iconLeft={iconLeft ? createElement(iconLeft) : undefined} + {...addMenuPortalProps()} + /> + + )} /> diff --git a/packages/components/fields/select-field/src/select-field.tsx b/packages/components/fields/select-field/src/select-field.tsx index 95329e7e3c..fc03a9c1b2 100644 --- a/packages/components/fields/select-field/src/select-field.tsx +++ b/packages/components/fields/select-field/src/select-field.tsx @@ -167,6 +167,8 @@ export type TSelectFieldProps = { menuPortalTarget?: ReactSelectProps['menuPortalTarget']; /** * z-index value for the menu portal + *
+ * Use in conjunction with `menuPortalTarget` */ menuPortalZIndex?: number; /** @@ -344,6 +346,7 @@ export default class SelectField extends Component { `SelectField: "touched" is expected to be an array of booleans when "isMulti" is true, instead got ${this.props.touched}.` ); } + return ( diff --git a/packages/components/inputs/async-creatable-select-input/README.md b/packages/components/inputs/async-creatable-select-input/README.md index 1131e30af3..324be143d1 100644 --- a/packages/components/inputs/async-creatable-select-input/README.md +++ b/packages/components/inputs/async-creatable-select-input/README.md @@ -83,7 +83,7 @@ export default Example; | `isSearchable` | `AsyncCreatableProps['isSearchable']` | | `true` | Whether to enable search functionality
[Props from React select was used](https://react-select.com/props) | | `maxMenuHeight` | `AsyncCreatableProps['maxMenuHeight']` | | | Maximum height of the menu before scrolling
[Props from React select was used](https://react-select.com/props) | | `menuPortalTarget` | `AsyncCreatableProps['menuPortalTarget']` | | | Dom element to portal the select menu to
[Props from React select was used](https://react-select.com/props) | -| `menuPortalZIndex` | `number` | | `1` | z-index value for the menu portal | +| `menuPortalZIndex` | `number` | | `1` | z-index value for the menu portal
Use in conjunction with `menuPortalTarget` | | `menuShouldBlockScroll` | `AsyncCreatableProps['menuShouldBlockScroll']` | | | whether the menu should block scroll while open
[Props from React select was used](https://react-select.com/props) | | `closeMenuOnSelect` | `AsyncCreatableProps['closeMenuOnSelect']` | | | Whether the menu should close after a value is selected. Defaults to `true`.
[Props from React select was used](https://react-select.com/props) | | `name` | `AsyncCreatableProps['name']` | | | Name of the HTML Input (optional - without this, no input will be rendered)
[Props from React select was used](https://react-select.com/props) | diff --git a/packages/components/inputs/async-creatable-select-input/src/async-creatable-select-input.story.js b/packages/components/inputs/async-creatable-select-input/src/async-creatable-select-input.story.js index 5bbe1505a0..6879966f8a 100644 --- a/packages/components/inputs/async-creatable-select-input/src/async-creatable-select-input.story.js +++ b/packages/components/inputs/async-creatable-select-input/src/async-creatable-select-input.story.js @@ -10,7 +10,10 @@ import { number, } from '@storybook/addon-knobs/react'; import Constraints from '@commercetools-uikit/constraints'; +import Spacings from '@commercetools-uikit/spacings'; import Section from '../../../../../docs/.storybook/decorators/section'; +import NeighbouringStackingContext from '../../../../../docs/.storybook/decorators/neighbouring-stacking-context'; +import { addMenuPortalProps } from '../../../../../docs/.storybook/utils'; import Readme from '../README.md'; import AsyncCreatableSelectInput from './async-creatable-select-input'; import * as icons from '../../../icons'; @@ -70,7 +73,7 @@ class SelectStory extends Component { key={`${isMulti}-${defaultOptions}`} defaultValue={isMulti ? [] : undefined} render={(value, onChange) => ( -
+ -
+ +
)} /> diff --git a/packages/components/inputs/async-creatable-select-input/src/async-creatable-select-input.tsx b/packages/components/inputs/async-creatable-select-input/src/async-creatable-select-input.tsx index 7227d33b11..5e932934bc 100644 --- a/packages/components/inputs/async-creatable-select-input/src/async-creatable-select-input.tsx +++ b/packages/components/inputs/async-creatable-select-input/src/async-creatable-select-input.tsx @@ -19,6 +19,7 @@ import { customComponentsWithIcons, messages, createSelectStyles, + warnIfMenuPortalPropsAreMissing, } from '@commercetools-uikit/select-utils'; import { filterDataAttributes, warning } from '@commercetools-uikit/utils'; import { useTheme } from '@commercetools-uikit/design-system'; @@ -204,6 +205,8 @@ export type TAsyncCreatableSelectInputProps = { menuPortalTarget?: ReactSelectAsyncCreatableProps['menuPortalTarget']; /** * z-index value for the menu portal + *
+ * Use in conjunction with `menuPortalTarget` */ menuPortalZIndex: number; /** @@ -369,6 +372,12 @@ const AsyncCreatableSelectInput = (props: TAsyncCreatableSelectInputProps) => { ); } + warnIfMenuPortalPropsAreMissing({ + menuPortalZIndex: props.menuPortalZIndex, + menuPortalTarget: props.menuPortalTarget, + componentName: 'AsyncCreatableSelectInput', + }); + return (
diff --git a/packages/components/inputs/async-select-input/README.md b/packages/components/inputs/async-select-input/README.md index 7d7800bee0..789ce1a549 100644 --- a/packages/components/inputs/async-select-input/README.md +++ b/packages/components/inputs/async-select-input/README.md @@ -82,7 +82,7 @@ export default Example; | `isSearchable` | `AsyncProps['isSearchable']` | | `true` | Whether to enable search functionality
[Props from React select was used](https://react-select.com/props) | | `maxMenuHeight` | `AsyncProps['maxMenuHeight']` | | | Maximum height of the menu before scrolling
[Props from React select was used](https://react-select.com/props) | | `menuPortalTarget` | `AsyncProps['menuPortalTarget']` | | | Dom element to portal the select menu to
[Props from React select was used](https://react-select.com/props) | -| `menuPortalZIndex` | `number` | | `1` | z-index value for the menu portal | +| `menuPortalZIndex` | `number` | | `1` | z-index value for the menu portal
Use in conjunction with `menuPortalTarget` | | `menuShouldBlockScroll` | `AsyncProps['menuShouldBlockScroll']` | | | whether the menu should block scroll while open
[Props from React select was used](https://react-select.com/props) | | `closeMenuOnSelect` | `AsyncProps['closeMenuOnSelect']` | | | Whether the menu should close after a value is selected. Defaults to `true`.
[Props from React select was used](https://react-select.com/props) | | `name` | `AsyncProps['name']` | | | Name of the HTML Input (optional - without this, no input will be rendered)
[Props from React select was used](https://react-select.com/props) | diff --git a/packages/components/inputs/async-select-input/src/async-select-input.story.js b/packages/components/inputs/async-select-input/src/async-select-input.story.js index 6c7be8406a..3a997ed066 100644 --- a/packages/components/inputs/async-select-input/src/async-select-input.story.js +++ b/packages/components/inputs/async-select-input/src/async-select-input.story.js @@ -10,7 +10,10 @@ import { number, } from '@storybook/addon-knobs/react'; import Constraints from '@commercetools-uikit/constraints'; +import Spacings from '@commercetools-uikit/spacings'; import Section from '../../../../../docs/.storybook/decorators/section'; +import NeighbouringStackingContext from '../../../../../docs/.storybook/decorators/neighbouring-stacking-context'; +import { addMenuPortalProps } from '../../../../../docs/.storybook/utils'; import Readme from '../README.md'; import AsyncSelectInput from './async-select-input'; import * as icons from '../../../icons'; @@ -71,7 +74,7 @@ class SelectStory extends Component { key={`${isMulti}-${defaultOptions}`} defaultValue={isMulti ? [] : undefined} render={(value, onChange) => ( -
+ -
+ + )} /> diff --git a/packages/components/inputs/async-select-input/src/async-select-input.tsx b/packages/components/inputs/async-select-input/src/async-select-input.tsx index 5efff1f311..c3591607d1 100644 --- a/packages/components/inputs/async-select-input/src/async-select-input.tsx +++ b/packages/components/inputs/async-select-input/src/async-select-input.tsx @@ -19,6 +19,7 @@ import { customComponentsWithIcons, messages, createSelectStyles, + warnIfMenuPortalPropsAreMissing, } from '@commercetools-uikit/select-utils'; import { useTheme } from '@commercetools-uikit/design-system'; @@ -196,6 +197,8 @@ export type TAsyncSelectInputProps = { menuPortalTarget?: ReactSelectAsyncProps['menuPortalTarget']; /** * z-index value for the menu portal + *
+ * Use in conjunction with `menuPortalTarget` */ menuPortalZIndex: number; /** @@ -318,6 +321,12 @@ const AsyncSelectInput = (props: TAsyncSelectInputProps) => { ); } + warnIfMenuPortalPropsAreMissing({ + menuPortalZIndex: props.menuPortalZIndex, + menuPortalTarget: props.menuPortalTarget, + componentName: 'AsyncSelectInput', + }); + const placeholder = props.placeholder || intl.formatMessage(messages.placeholder); diff --git a/packages/components/inputs/creatable-select-input/README.md b/packages/components/inputs/creatable-select-input/README.md index 7c1052a7b5..592e3fcd28 100644 --- a/packages/components/inputs/creatable-select-input/README.md +++ b/packages/components/inputs/creatable-select-input/README.md @@ -77,7 +77,7 @@ export default Example; | `isSearchable` | `CreatableProps['isSearchable']` | | `true` | Whether to enable search functionality
[Props from React select was used](https://react-select.com/props#creatable-props) | | `maxMenuHeight` | `CreatableProps['maxMenuHeight']` | | | Maximum height of the menu before scrolling
[Props from React select was used](https://react-select.com/props#creatable-props) | | `menuPortalTarget` | `CreatableProps['menuPortalTarget']` | | | Dom element to portal the select menu to
[Props from React select was used](https://react-select.com/props#creatable-props) | -| `menuPortalZIndex` | `number` | | `1` | z-index value for the menu portal | +| `menuPortalZIndex` | `number` | | `1` | z-index value for the menu portal
Use in conjunction with `menuPortalTarget` | | `menuShouldBlockScroll` | `CreatableProps['menuShouldBlockScroll']` | | | whether the menu should block scroll while open
[Props from React select was used](https://react-select.com/props#creatable-props) | | `closeMenuOnSelect` | `CreatableProps['closeMenuOnSelect']` | | | Whether the menu should close after a value is selected. Defaults to `true`.
[Props from React select was used](https://react-select.com/props#creatable-props) | | `name` | `CreatableProps['name']` | | | Name of the HTML Input (optional - without this, no input will be rendered)
[Props from React select was used](https://react-select.com/props#creatable-props) | diff --git a/packages/components/inputs/creatable-select-input/src/creatable-select-input.story.js b/packages/components/inputs/creatable-select-input/src/creatable-select-input.story.js index c79d3be2c1..df4dff3ac4 100644 --- a/packages/components/inputs/creatable-select-input/src/creatable-select-input.story.js +++ b/packages/components/inputs/creatable-select-input/src/creatable-select-input.story.js @@ -13,6 +13,8 @@ import Constraints from '@commercetools-uikit/constraints'; import Spacings from '@commercetools-uikit/spacings'; import LinkTo from '@storybook/addon-links/react'; import Section from '../../../../../docs/.storybook/decorators/section'; +import NeighbouringStackingContext from '../../../../../docs/.storybook/decorators/neighbouring-stacking-context'; +import { addMenuPortalProps } from '../../../../../docs/.storybook/utils'; import Readme from '../README.md'; import CreatableSelectInput from './creatable-select-input'; import * as icons from '../../../icons'; @@ -103,53 +105,57 @@ storiesOf('Components|Inputs/SelectInputs', module) key={isMulti} defaultValue={isMulti ? [] : undefined} render={(value, onChange) => ( - { - action('onChange')(event, ...args); - onChange(event.target.value); - }} - onFocus={action('onFocus')} - onInputChange={action('onInputChange')} - options={options} - placeholder={text('placeholder', 'Select..')} - tabIndex={text('tabIndex', '0')} - tabSelectsValue={boolean('tabSelectsValue', true)} - value={value} - // Creatable props - allowCreateWhileLoading={boolean( - 'allowCreateWhileLoading', - false - )} - createOptionPosition={select( - 'createOptionPosition', - ['first', 'last'], - 'last' - )} - showOptionGroupDivider={showOptionGroupDivider} - iconLeft={iconLeft ? createElement(iconLeft) : undefined} - /> + + { + action('onChange')(event, ...args); + onChange(event.target.value); + }} + onFocus={action('onFocus')} + onInputChange={action('onInputChange')} + options={options} + placeholder={text('placeholder', 'Select..')} + tabIndex={text('tabIndex', '0')} + tabSelectsValue={boolean('tabSelectsValue', true)} + value={value} + // Creatable props + allowCreateWhileLoading={boolean( + 'allowCreateWhileLoading', + false + )} + createOptionPosition={select( + 'createOptionPosition', + ['first', 'last'], + 'last' + )} + showOptionGroupDivider={showOptionGroupDivider} + iconLeft={iconLeft ? createElement(iconLeft) : undefined} + {...addMenuPortalProps()} + /> + + )} /> diff --git a/packages/components/inputs/creatable-select-input/src/creatable-select-input.tsx b/packages/components/inputs/creatable-select-input/src/creatable-select-input.tsx index dd94334c2c..059a35393f 100644 --- a/packages/components/inputs/creatable-select-input/src/creatable-select-input.tsx +++ b/packages/components/inputs/creatable-select-input/src/creatable-select-input.tsx @@ -204,6 +204,8 @@ export type TCreatableSelectInputProps = { menuPortalTarget?: ReactSelectCreatableProps['menuPortalTarget']; /** * z-index value for the menu portal + *
+ * Use in conjunction with `menuPortalTarget` */ menuPortalZIndex: number; /** diff --git a/packages/components/inputs/money-input/README.md b/packages/components/inputs/money-input/README.md index 2416cd6d48..68877f4988 100644 --- a/packages/components/inputs/money-input/README.md +++ b/packages/components/inputs/money-input/README.md @@ -66,7 +66,7 @@ export default Example; | `isAutofocussed` | `boolean` | | | Focus the input on initial render | | `onChange` | `Function`
[See signature.](#signature-onChange) | | | Called with the event of the input or dropdown when either the currency or the amount have changed. | | `menuPortalTarget` | `ReactSelectProps['menuPortalTarget']` | | | Dom element to portal the currency select menu to
[Props from React select was used](https://react-select.com/props) | -| `menuPortalZIndex` | `number` | | `1` | z-index value for the currency select menu portal | +| `menuPortalZIndex` | `number` | | `1` | z-index value for the currency select menu portal
Use in conjunction with `menuPortalTarget` | | `menuShouldBlockScroll` | `ReactSelectProps['menuShouldBlockScroll']` | | | whether the menu should block scroll while open
[Props from React select was used](https://react-select.com/props) | | `hasError` | `boolean` | | | Indicates that input has errors | | `hasWarning` | `boolean` | | | Control to indicate on the input if there are selected values that are potentially invalid | diff --git a/packages/components/inputs/money-input/src/money-input.story.js b/packages/components/inputs/money-input/src/money-input.story.js index 6d4d96ac0d..2e2ea0d9b7 100644 --- a/packages/components/inputs/money-input/src/money-input.story.js +++ b/packages/components/inputs/money-input/src/money-input.story.js @@ -10,7 +10,10 @@ import { select, } from '@storybook/addon-knobs/react'; import Constraints from '@commercetools-uikit/constraints'; +import Spacings from '@commercetools-uikit/spacings'; import Section from '../../../../../docs/.storybook/decorators/section'; +import NeighbouringStackingContext from '../../../../../docs/.storybook/decorators/neighbouring-stacking-context'; +import { addMenuPortalProps } from '../../../../../docs/.storybook/utils'; import Readme from '../README.md'; import MoneyInput from './money-input'; @@ -57,37 +60,41 @@ class MoneyInputStory extends Component { return ( <>
- { - action('onChange')(event); + + { + action('onChange')(event); - if (event.target.name.endsWith('.amount')) { - this.setState({ amount: event.target.value }); - } + if (event.target.name.endsWith('.amount')) { + this.setState({ amount: event.target.value }); + } - if (event.target.name.endsWith('.currencyCode')) { - this.setState({ currencyCode: event.target.value }); - } - }} - hasError={boolean('hasError', false)} - hasWarning={boolean('hasWarning', false)} - horizontalConstraint={select( - 'horizontalConstraint', - Constraints.getAcceptedMaxPropValues(3), - 7 - )} - hasHighPrecisionBadge={boolean('hasHighPrecisionBadge', false)} - /> + if (event.target.name.endsWith('.currencyCode')) { + this.setState({ currencyCode: event.target.value }); + } + }} + hasError={boolean('hasError', false)} + hasWarning={boolean('hasWarning', false)} + horizontalConstraint={select( + 'horizontalConstraint', + Constraints.getAcceptedMaxPropValues(3), + 7 + )} + hasHighPrecisionBadge={boolean('hasHighPrecisionBadge', false)} + {...addMenuPortalProps()} + /> + +

diff --git a/packages/components/inputs/money-input/src/money-input.tsx b/packages/components/inputs/money-input/src/money-input.tsx index 6df587c788..a5a02d0c4f 100644 --- a/packages/components/inputs/money-input/src/money-input.tsx +++ b/packages/components/inputs/money-input/src/money-input.tsx @@ -21,6 +21,7 @@ import Tooltip from '@commercetools-uikit/tooltip'; import { DropdownIndicator, createSelectStyles, + warnIfMenuPortalPropsAreMissing, } from '@commercetools-uikit/select-utils'; import { FractionDigitsIcon } from '@commercetools-uikit/icons'; import Constraints from '@commercetools-uikit/constraints'; @@ -492,6 +493,8 @@ type TMoneyInputProps = { menuPortalTarget?: ReactSelectProps['menuPortalTarget']; /** * z-index value for the currency select menu portal + *
+ * Use in conjunction with `menuPortalTarget` */ menuPortalZIndex?: number; /** @@ -561,6 +564,12 @@ const MoneyInput = (props: TMoneyInputProps) => { ); } + warnIfMenuPortalPropsAreMissing({ + menuPortalZIndex: props.menuPortalZIndex, + menuPortalTarget: props.menuPortalTarget, + componentName: 'MoneyInput', + }); + const { onFocus } = props; const handleAmountFocus = useCallback(() => { if (onFocus) diff --git a/packages/components/inputs/search-select-input/README.md b/packages/components/inputs/search-select-input/README.md index 7c51cf2ddb..5e980e29f3 100644 --- a/packages/components/inputs/search-select-input/README.md +++ b/packages/components/inputs/search-select-input/README.md @@ -86,7 +86,7 @@ export default Example; | `noOptionsMessage` | `AsyncProps['noOptionsMessage']` | | | Can be used to render a custom value when there are no options (either because of no search results, or all options have been used, or there were none in the first place). Gets called with `{ inputValue: String }`. `inputValue` will be an empty string when no search text is present.
[Props from React select was used](https://react-select.com/props) | | `maxMenuHeight` | `AsyncProps['maxMenuHeight']` | | `220` | Maximum height of the menu before scrolling
[Props from React select was used](https://react-select.com/props) | | `menuPortalTarget` | `AsyncProps['menuPortalTarget']` | | | Dom element to portal the select menu to
[Props from React select was used](https://react-select.com/props) | -| `menuPortalZIndex` | `number` | | `1` | z-index value for the menu portal | +| `menuPortalZIndex` | `number` | | `1` | z-index value for the menu portal
Use in conjunction with `menuPortalTarget` | | `menuShouldBlockScroll` | `AsyncProps['menuShouldBlockScroll']` | | | whether the menu should block scroll while open
[Props from React select was used](https://react-select.com/props) | | `closeMenuOnSelect` | `AsyncProps['closeMenuOnSelect']` | | | Whether the menu should close after a value is selected. Defaults to `true`.
[Props from React select was used](https://react-select.com/props) | | `showOptionGroupDivider` | `boolean` | | | Determines if option groups will be separated by a divider | diff --git a/packages/components/inputs/search-select-input/src/search-select-input.story.js b/packages/components/inputs/search-select-input/src/search-select-input.story.js index 5316ee055b..f0850f1145 100644 --- a/packages/components/inputs/search-select-input/src/search-select-input.story.js +++ b/packages/components/inputs/search-select-input/src/search-select-input.story.js @@ -10,8 +10,11 @@ import { number, } from '@storybook/addon-knobs/react'; import Constraints from '@commercetools-uikit/constraints'; +import Spacings from '@commercetools-uikit/spacings'; import { SELECT_DROPDOWN_OPTION_TYPES } from '@commercetools-uikit/select-utils'; import Section from '../../../../../docs/.storybook/decorators/section'; +import NeighbouringStackingContext from '../../../../../docs/.storybook/decorators/neighbouring-stacking-context'; +import { addMenuPortalProps } from '../../../../../docs/.storybook/utils'; import SearchSelectInput from './search-select-input'; import Readme from '../README.md'; @@ -68,7 +71,7 @@ class SearchSelectInputStory extends Component { key={`${isMulti}`} defaultValue={isMulti ? [] : undefined} render={(value, onChange) => ( -

+ +

In this example, our `loadOptions` function uses the data @@ -125,7 +130,7 @@ class SearchSelectInputStory extends Component { Data used:

{JSON.stringify(colourOptions, undefined, 2)}
-
+ )} />
diff --git a/packages/components/inputs/search-select-input/src/search-select-input.tsx b/packages/components/inputs/search-select-input/src/search-select-input.tsx index 6faa0a5be6..a39e86bfe3 100644 --- a/packages/components/inputs/search-select-input/src/search-select-input.tsx +++ b/packages/components/inputs/search-select-input/src/search-select-input.tsx @@ -7,6 +7,7 @@ import { warning } from '@commercetools-uikit/utils'; import { CustomSelectInputOption, SearchIconDropdownIndicator, + warnIfMenuPortalPropsAreMissing, } from '@commercetools-uikit/select-utils'; import messages from './messages'; import { SearchSelectInputWrapper } from './search-select-input.styles'; @@ -171,6 +172,8 @@ export type TSearchSelectInputProps = { menuPortalTarget?: ReactSelectAsyncProps['menuPortalTarget']; /** * z-index value for the menu portal + *
+ * Use in conjunction with `menuPortalTarget` */ menuPortalZIndex: number; /** @@ -280,6 +283,12 @@ const SearchSelectInput = (props: TSearchSelectInputProps) => { ); } + warnIfMenuPortalPropsAreMissing({ + menuPortalZIndex: props.menuPortalZIndex, + menuPortalTarget: props.menuPortalTarget, + componentName: 'SearchSelectInput', + }); + const noOptionsMessage = props.noOptionsMessage || (() => intl.formatMessage(messages.noOptionsMessage)); diff --git a/packages/components/inputs/select-input/README.md b/packages/components/inputs/select-input/README.md index 2cbd9bb176..11df9aeb57 100644 --- a/packages/components/inputs/select-input/README.md +++ b/packages/components/inputs/select-input/README.md @@ -84,7 +84,7 @@ export default Example; | `isSearchable` | `ReactSelectProps['isSearchable']` | | | Whether to enable search functionality
[Props from React select was used](https://react-select.com/props) | | `maxMenuHeight` | `ReactSelectProps['maxMenuHeight']` | | `220` | Maximum height of the menu before scrolling
[Props from React select was used](https://react-select.com/props) | | `menuPortalTarget` | `ReactSelectProps['menuPortalTarget']` | | | Dom element to portal the select menu to
[Props from React select was used](https://react-select.com/props) | -| `menuPortalZIndex` | `number` | | `1` | z-index value for the menu portal | +| `menuPortalZIndex` | `number` | | `1` | z-index value for the menu portal
Use in conjunction with `menuPortalTarget` | | `menuShouldBlockScroll` | `ReactSelectProps['menuShouldBlockScroll']` | | | whether the menu should block scroll while open
[Props from React select was used](https://react-select.com/props) | | `closeMenuOnSelect` | `ReactSelectProps['closeMenuOnSelect']` | | | Whether the menu should close after a value is selected. Defaults to `true`.
[Props from React select was used](https://react-select.com/props) | | `name` | `ReactSelectProps['name']` | | | Name of the HTML Input (optional - without this, no input will be rendered)
[Props from React select was used](https://react-select.com/props) | diff --git a/packages/components/inputs/select-input/src/select-input.story.js b/packages/components/inputs/select-input/src/select-input.story.js index 78d8a56505..49d734d262 100644 --- a/packages/components/inputs/select-input/src/select-input.story.js +++ b/packages/components/inputs/select-input/src/select-input.story.js @@ -13,6 +13,8 @@ import Constraints from '@commercetools-uikit/constraints'; import Spacings from '@commercetools-uikit/spacings'; import LinkTo from '@storybook/addon-links/react'; import Section from '../../../../../docs/.storybook/decorators/section'; +import NeighbouringStackingContext from '../../../../../docs/.storybook/decorators/neighbouring-stacking-context'; +import { addMenuPortalProps } from '../../../../../docs/.storybook/utils'; import Readme from '../README.md'; import SelectInput from './select-input'; import * as icons from '../../../icons'; @@ -106,47 +108,51 @@ storiesOf('Components|Inputs/SelectInputs', module) key={isMulti} defaultValue={isMulti ? [] : undefined} render={(value, onChange) => ( - { - action('onChange')(event, ...args); - onChange(event.target.value); - }} - onFocus={action('onFocus')} - onInputChange={action('onInputChange')} - options={options} - placeholder={text('placeholder', 'Select..')} - tabIndex={text('tabIndex', '0')} - tabSelectsValue={boolean('tabSelectsValue', true)} - value={value} - showOptionGroupDivider={showOptionGroupDivider} - iconLeft={iconLeft ? createElement(iconLeft) : undefined} - /> + + { + action('onChange')(event, ...args); + onChange(event.target.value); + }} + onFocus={action('onFocus')} + onInputChange={action('onInputChange')} + options={options} + placeholder={text('placeholder', 'Select..')} + tabIndex={text('tabIndex', '0')} + tabSelectsValue={boolean('tabSelectsValue', true)} + value={value} + showOptionGroupDivider={showOptionGroupDivider} + iconLeft={iconLeft ? createElement(iconLeft) : undefined} + {...addMenuPortalProps()} + /> + + )} /> diff --git a/packages/components/inputs/select-input/src/select-input.tsx b/packages/components/inputs/select-input/src/select-input.tsx index a1f1c7c750..cfc5c7d60d 100644 --- a/packages/components/inputs/select-input/src/select-input.tsx +++ b/packages/components/inputs/select-input/src/select-input.tsx @@ -16,6 +16,7 @@ import { customComponentsWithIcons, createSelectStyles, messages, + warnIfMenuPortalPropsAreMissing, } from '@commercetools-uikit/select-utils'; import { filterDataAttributes } from '@commercetools-uikit/utils'; import { useTheme } from '@commercetools-uikit/design-system'; @@ -228,6 +229,8 @@ export type TSelectInputProps = { menuPortalTarget?: ReactSelectProps['menuPortalTarget']; /** * z-index value for the menu portal + *
+ * Use in conjunction with `menuPortalTarget` */ menuPortalZIndex?: number; /** @@ -333,6 +336,12 @@ const SelectInput = (props: TSelectInputProps) => { const intl = useIntl(); const { isNewTheme } = useTheme(); + warnIfMenuPortalPropsAreMissing({ + menuPortalZIndex: props.menuPortalZIndex, + menuPortalTarget: props.menuPortalTarget, + componentName: 'SelectInput', + }); + const placeholder = props.placeholder || intl.formatMessage(messages.placeholder); // Options can be grouped as diff --git a/packages/components/inputs/select-utils/src/index.ts b/packages/components/inputs/select-utils/src/index.ts index b651354c85..23ebfd18bf 100644 --- a/packages/components/inputs/select-utils/src/index.ts +++ b/packages/components/inputs/select-utils/src/index.ts @@ -8,5 +8,6 @@ export * from './custom-styled-select-options'; export * from './export-types'; export { default as messages } from './messages'; export { default as createSelectStyles } from './create-select-styles'; +export { warnIfMenuPortalPropsAreMissing } from './warning'; export { default as version } from './version'; diff --git a/packages/components/inputs/select-utils/src/warning.ts b/packages/components/inputs/select-utils/src/warning.ts new file mode 100644 index 0000000000..a18f056004 --- /dev/null +++ b/packages/components/inputs/select-utils/src/warning.ts @@ -0,0 +1,22 @@ +import { warning } from '@commercetools-uikit/utils'; +import { type Props as ReactSelectProps } from 'react-select'; + +const getMessage = (componentName: string) => + `${componentName}: use \`menuPortalZIndex\` in conjunction with \`menuPortalTarget\``; + +type TWarnIfMenuPortalPropsAreMissingProps = { + menuPortalZIndex?: number; + menuPortalTarget?: ReactSelectProps['menuPortalTarget']; + componentName: string; +}; + +export const warnIfMenuPortalPropsAreMissing = ( + props: TWarnIfMenuPortalPropsAreMissingProps +): void => { + if ( + typeof props.menuPortalZIndex !== 'undefined' && + props.menuPortalZIndex !== 1 // 1 is the value passed in default props + ) { + warning(props.menuPortalTarget, getMessage(props.componentName)); + } +}; diff --git a/packages/components/inputs/selectable-search-input/README.md b/packages/components/inputs/selectable-search-input/README.md index 6d2acff7c3..b1e58258c3 100644 --- a/packages/components/inputs/selectable-search-input/README.md +++ b/packages/components/inputs/selectable-search-input/README.md @@ -77,7 +77,7 @@ export default Example; | `isClearable` | `boolean` | | `true` | Indicates if the input should be cleared when the clear button is clicked. Defaults to true. | | `horizontalConstraint` | `union`
Possible values:
`10 , 11 , 12 , 13 , 14 , 15 , 16 , 'scale' , 'auto'` | | `'scale'` | Horizontal size limit of the input fields. | | `options` | `union`
Possible values:
`TOption[] , TOptionObject[]` | ✅ | | Array of options that populate the select menu | -| `menuPortalZIndex` | `number` | | `1` | z-index value for the menu portal | +| `menuPortalZIndex` | `number` | | `1` | z-index value for the menu portal
Use in conjunction with `menuPortalTarget` | | `menuPortalTarget` | `ReactSelectProps['menuPortalTarget']` | | | Dom element to portal the select menu to
[Props from React select was used](https://react-select.com/props) | | `menuShouldBlockScroll` | `ReactSelectProps['menuShouldBlockScroll']` | | | whether the menu should block scroll while open
[Props from React select was used](https://react-select.com/props) | | `onMenuInputChange` | `ReactSelectProps['onInputChange']` | | | Handle change events on the menu input
[Props from React select was used](https://react-select.com/props) | diff --git a/packages/components/inputs/selectable-search-input/src/selectable-search-input.story.js b/packages/components/inputs/selectable-search-input/src/selectable-search-input.story.js index 77993e4600..cbaae086e2 100644 --- a/packages/components/inputs/selectable-search-input/src/selectable-search-input.story.js +++ b/packages/components/inputs/selectable-search-input/src/selectable-search-input.story.js @@ -3,7 +3,10 @@ import { storiesOf } from '@storybook/react'; import { action } from '@storybook/addon-actions'; import { withKnobs, boolean, text, select } from '@storybook/addon-knobs/react'; import Constraints from '@commercetools-uikit/constraints'; +import Spacings from '@commercetools-uikit/spacings'; import Section from '../../../../../docs/.storybook/decorators/section'; +import NeighbouringStackingContext from '../../../../../docs/.storybook/decorators/neighbouring-stacking-context'; +import { addMenuPortalProps } from '../../../../../docs/.storybook/utils'; import Readme from '../README.md'; import SelectableSearchInput from './selectable-search-input'; @@ -104,42 +107,46 @@ storiesOf('Components|Inputs', module) null )} > - { - action('onChange')(event); - if (event.target.name.endsWith('.textInput')) { - setTextInputValue(event.target.value); - } - if (event.target.name.endsWith('.dropdown')) { - setDropdownValue(event.target.value); - } - }} - isAutofocussed={boolean('isAutofocussed', false)} - isDisabled={boolean('isDisabled', false)} - isReadOnly={boolean('isReadOnly', false)} - isClearable={boolean('isClearable', true)} - showSubmitButton={boolean('showSubmitButton', true)} - hasError={boolean('hasError', false)} - hasWarning={boolean('hasWarning', false)} - placeholder={text('placeholder', 'Placeholder')} - horizontalConstraint={select( - 'horizontalConstraint', - Constraints.getAcceptedMaxPropValues(10), - 16 - )} - menuHorizontalConstraint={select( - 'menuHorizontalConstraint', - Constraints.getAcceptedMaxPropValues(3, 5), - 3 - )} - options={options} - onSubmit={(submitValues) => { - alert(JSON.stringify(submitValues)); - }} - /> + + { + action('onChange')(event); + if (event.target.name.endsWith('.textInput')) { + setTextInputValue(event.target.value); + } + if (event.target.name.endsWith('.dropdown')) { + setDropdownValue(event.target.value); + } + }} + isAutofocussed={boolean('isAutofocussed', false)} + isDisabled={boolean('isDisabled', false)} + isReadOnly={boolean('isReadOnly', false)} + isClearable={boolean('isClearable', true)} + showSubmitButton={boolean('showSubmitButton', true)} + hasError={boolean('hasError', false)} + hasWarning={boolean('hasWarning', false)} + placeholder={text('placeholder', 'Placeholder')} + horizontalConstraint={select( + 'horizontalConstraint', + Constraints.getAcceptedMaxPropValues(10), + 16 + )} + menuHorizontalConstraint={select( + 'menuHorizontalConstraint', + Constraints.getAcceptedMaxPropValues(3, 5), + 3 + )} + options={options} + onSubmit={(submitValues) => { + alert(JSON.stringify(submitValues)); + }} + {...addMenuPortalProps()} + /> + + ); }); diff --git a/packages/components/inputs/selectable-search-input/src/selectable-search-input.tsx b/packages/components/inputs/selectable-search-input/src/selectable-search-input.tsx index 1598f59568..e0506f6c03 100644 --- a/packages/components/inputs/selectable-search-input/src/selectable-search-input.tsx +++ b/packages/components/inputs/selectable-search-input/src/selectable-search-input.tsx @@ -18,6 +18,7 @@ import { filterDataAttributes, warning, } from '@commercetools-uikit/utils'; +import { warnIfMenuPortalPropsAreMissing } from '@commercetools-uikit/select-utils'; import { getClearIconButtonStyles, getSearchIconButtonStyles, @@ -150,6 +151,8 @@ export type TSelectableSearchInputProps = { options: TOption[] | TOptionObject[]; /** * z-index value for the menu portal + *
+ * Use in conjunction with `menuPortalTarget` */ menuPortalZIndex?: number; /** @@ -251,10 +254,16 @@ const SelectableSearchInput = (props: TSelectableSearchInputProps) => { if (!props.isReadOnly) { warning( typeof props.onChange === 'function', - 'TextInput: `onChange` is required when is not read only.' + 'SelectableSearchInput: `onChange` is required when is not read only.' ); } + warnIfMenuPortalPropsAreMissing({ + menuPortalZIndex: props.menuPortalZIndex, + menuPortalTarget: props.menuPortalTarget, + componentName: 'SelectableSearchInput', + }); + const { onFocus, onBlur, name } = props; const handleTextInputFocus = useCallback(() => { if (onFocus) {