diff --git a/packages/pf4-component-mapper/demo/demo-schemas/select-schema.js b/packages/pf4-component-mapper/demo/demo-schemas/select-schema.js
new file mode 100644
index 000000000..f0b085823
--- /dev/null
+++ b/packages/pf4-component-mapper/demo/demo-schemas/select-schema.js
@@ -0,0 +1,136 @@
+import componentTypes from '@data-driven-forms/react-form-renderer/dist/cjs/component-types';
+
+const options = [
+ {
+ label: 'Morton',
+ value: 'Jenifer'
+ },
+ {
+ label: 'Vega',
+ value: 'Cervantes'
+ },
+ {
+ label: 'Gilbert',
+ value: 'Wallace'
+ },
+ {
+ label: 'Jami',
+ value: 'Cecilia'
+ },
+ {
+ label: 'Ebony',
+ value: 'Kay'
+ }
+];
+
+const loadOptions = (inputValue = '') => {
+ return new Promise((res) =>
+ setTimeout(() => {
+ if (inputValue.length === 0) {
+ return res(options.slice(0, 3));
+ }
+
+ return res(options.filter(({ label }) => label.toLocaleLowerCase().includes(inputValue.toLocaleLowerCase())));
+ }, 1500)
+ );
+};
+
+const selectSchema = {
+ fields: [
+ {
+ component: componentTypes.SELECT,
+ name: 'simple-portal-select',
+ label: 'Simple portal select',
+ options,
+ menuIsPortal: true
+ },
+ {
+ component: componentTypes.SELECT,
+ name: 'simple-async-select',
+ label: 'Simple async select',
+ loadOptions
+ },
+ {
+ component: componentTypes.SELECT,
+ name: 'simple-searchable-async-select',
+ label: 'Simple searchable async select',
+ loadOptions,
+ isSearchable: true
+ },
+ {
+ component: componentTypes.SELECT,
+ name: 'multi-async-select',
+ label: 'multi async select',
+ loadOptions,
+ isMulti: true
+ },
+ {
+ component: componentTypes.SELECT,
+ name: 'searchable-multi-async-select',
+ label: 'Multi searchable async select',
+ loadOptions,
+ isSearchable: true
+ },
+ {
+ component: componentTypes.SELECT,
+ name: 'multi-simple-select',
+ label: 'Simple multi select',
+ options,
+ isMulti: true
+ },
+ {
+ component: componentTypes.SELECT,
+ name: 'multi-searchable-select',
+ label: 'Searchable multi select',
+ options,
+ isMulti: true,
+ isSearchable: true
+ },
+ {
+ component: componentTypes.SELECT,
+ name: 'multi-clearable-searchable-select',
+ label: 'Searchable clearable multi select',
+ options,
+ isMulti: true,
+ isSearchable: true,
+ isClearable: true
+ },
+ {
+ component: componentTypes.SELECT,
+ name: 'simple-select',
+ label: 'Simple-select',
+ options
+ },
+ {
+ component: componentTypes.SELECT,
+ name: 'disabled-select',
+ label: 'Disabled-select',
+ options,
+ isDisabled: true
+ },
+ {
+ component: componentTypes.SELECT,
+ name: 'clearable-select',
+ label: 'Clearable-select',
+ options,
+ isClearable: true
+ },
+ {
+ component: componentTypes.SELECT,
+ name: 'searchable-select',
+ label: 'Clearable-select',
+ options,
+ isSearchable: true
+ },
+ {
+ component: componentTypes.SELECT,
+ name: 'dosbaled-option-select',
+ label: 'Disabled-option-select',
+ options: [...options, { label: 'Disabled option', value: 'disabled', isDisabled: true }]
+ }
+ ]
+};
+
+export default {
+ ...selectSchema
+};
diff --git a/packages/pf4-component-mapper/demo/index.js b/packages/pf4-component-mapper/demo/index.js
index ae9a954f6..dbd0b4526 100644
--- a/packages/pf4-component-mapper/demo/index.js
+++ b/packages/pf4-component-mapper/demo/index.js
@@ -5,11 +5,12 @@ import FormRenderer from '@data-driven-forms/react-form-renderer';
import miqSchema from './demo-schemas/miq-schema';
import { uiArraySchema, arraySchema, array1Schema, schema, uiSchema, conditionalSchema, arraySchemaDDF } from './demo-schemas/widget-schema';
import { componentMapper, FormTemplate } from '../src';
-import { Title, Button, Toolbar, ToolbarGroup } from '@patternfly/react-core';
+import { Title, Button, Toolbar, ToolbarGroup, ToolbarItem, Modal } from '@patternfly/react-core';
import { wizardSchema, wizardSchemaWithFunction, wizardSchemaSimple, wizardSchemaSubsteps, wizardSchemaMoreSubsteps } from './demo-schemas/wizard-schema';
import sandboxSchema from './demo-schemas/sandbox';
import dualSchema from './demo-schemas/dual-list-schema';
import demoSchema from '@data-driven-forms/common/src/demoschema';
+import selectSchema from './demo-schemas/select-schema';
const Summary = props =>
Custom summary component.
;
@@ -23,7 +24,7 @@ const fieldArrayState = { schema: arraySchemaDDF, additionalOptions: {
class App extends React.Component {
constructor(props) {
super(props);
- this.state = fieldArrayState
+ this.state = {schema: selectSchema, additionalOptions: {}}
}
render() {
@@ -32,25 +33,30 @@ class App extends React.Component {
Pf4 component mapper
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
(
-
);
ClearIndicator.propTypes = {
- innerProps: PropTypes.object.isRequired,
- clearValue: PropTypes.func
-};
-
-ClearIndicator.defaultProps = {
- clearValue: () => undefined
+ clearSelection: PropTypes.func.isRequired
};
export default ClearIndicator;
diff --git a/packages/pf4-component-mapper/src/common/select/clear-indicator.scss b/packages/pf4-component-mapper/src/common/select/clear-indicator.scss
new file mode 100644
index 000000000..fe064681a
--- /dev/null
+++ b/packages/pf4-component-mapper/src/common/select/clear-indicator.scss
@@ -0,0 +1,17 @@
+.ddorg__pf4-component-mapper__select-clear-indicator {
+ position: relative;
+ display: inline-block;
+ > svg {
+ fill: var(--pf-global--palette--black-600)
+ }
+ &:hover > svg {
+ fill: inherit
+ }
+ &::before {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ }
+}
diff --git a/packages/pf4-component-mapper/src/common/select/dropdown-indicator.js b/packages/pf4-component-mapper/src/common/select/dropdown-indicator.js
deleted file mode 100644
index 1f41a67c2..000000000
--- a/packages/pf4-component-mapper/src/common/select/dropdown-indicator.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-import { CircleNotchIcon, CaretDownIcon } from '@patternfly/react-icons';
-
-const DropdownIndicator = ({ selectProps: { isFetching } }) => (isFetching ? : );
-
-DropdownIndicator.propTypes = {
- selectProps: PropTypes.shape({
- isFetching: PropTypes.bool
- }).isRequired
-};
-
-export default DropdownIndicator;
diff --git a/packages/pf4-component-mapper/src/common/select/empty-options.js b/packages/pf4-component-mapper/src/common/select/empty-options.js
new file mode 100644
index 000000000..9dc17885e
--- /dev/null
+++ b/packages/pf4-component-mapper/src/common/select/empty-options.js
@@ -0,0 +1,18 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+const EmptyOptions = ({ noOptionsMessage, noResultsMessage, getInputProps, isSearchable, isFetching }) => {
+ const { value } = getInputProps();
+ const message = isFetching ? noOptionsMessage() : isSearchable && value ? noResultsMessage : noOptionsMessage();
+ return {message}
;
+};
+
+EmptyOptions.propTypes = {
+ noOptionsMessage: PropTypes.func.isRequired,
+ noResultsMessage: PropTypes.node.isRequired,
+ getInputProps: PropTypes.func.isRequired,
+ isSearchable: PropTypes.bool,
+ isFetching: PropTypes.bool
+};
+
+export default EmptyOptions;
diff --git a/packages/pf4-component-mapper/src/common/select/input.js b/packages/pf4-component-mapper/src/common/select/input.js
index 2204a2a97..964f4caee 100644
--- a/packages/pf4-component-mapper/src/common/select/input.js
+++ b/packages/pf4-component-mapper/src/common/select/input.js
@@ -1,13 +1,39 @@
-import React from 'react';
-import { components } from 'react-select';
+import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
+import { Divider } from '@patternfly/react-core';
-const Input = (props) => ;
+import './input.scss';
+
+const Input = ({ inputRef, isSearchable, isDisabled, getInputProps, value, ...props }) => {
+ const inputProps = getInputProps({ disabled: isDisabled });
+ return (
+
+
+
+
+
+
+ );
+};
Input.propTypes = {
- selectProps: PropTypes.shape({
- isMulti: PropTypes.bool
- }).isRequired
+ inputRef: PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
+ isSearchable: PropTypes.bool,
+ isDisabled: PropTypes.bool,
+ getInputProps: PropTypes.func.isRequired,
+ value: PropTypes.string
};
export default Input;
diff --git a/packages/pf4-component-mapper/src/common/select/input.scss b/packages/pf4-component-mapper/src/common/select/input.scss
new file mode 100644
index 000000000..447275cdc
--- /dev/null
+++ b/packages/pf4-component-mapper/src/common/select/input.scss
@@ -0,0 +1,4 @@
+.ddorg__pf4-component-mapper__select-input {
+ border: none;
+ flex: 1;
+}
diff --git a/packages/pf4-component-mapper/src/common/select/menu.js b/packages/pf4-component-mapper/src/common/select/menu.js
new file mode 100644
index 000000000..c8c6ba08b
--- /dev/null
+++ b/packages/pf4-component-mapper/src/common/select/menu.js
@@ -0,0 +1,131 @@
+import React, { useEffect, useState, Fragment } from 'react';
+import { createPortal } from 'react-dom';
+import Option from './option';
+import Input from './input';
+import EmptyOption from './empty-options';
+
+const getScrollParent = (element) => {
+ let style = getComputedStyle(element);
+ const excludeStaticParent = style.position === 'absolute';
+ const overflowRx = /(auto|scroll)/;
+ const docEl = document.documentElement;
+
+ if (style.position === 'fixed') {
+ return docEl;
+ }
+
+ for (let parent = element; (parent = parent.parentElement);) { // eslint-disable-line
+ style = getComputedStyle(parent);
+ if (excludeStaticParent && style.position === 'static') {
+ continue;
+ }
+
+ if (overflowRx.test(style.overflow + style.overflowY + style.overflowX)) {
+ return parent;
+ }
+ }
+
+ return docEl;
+};
+
+const getMenuPosition = (selectBase) => {
+ if (!selectBase) {
+ return {};
+ }
+
+ return selectBase.getBoundingClientRect();
+};
+
+const MenuPortal = ({ selectToggleRef, menuPortalTarget, children, isSearchable }) => {
+ const [position, setPosition] = useState(getMenuPosition(selectToggleRef.current));
+ useEffect(() => {
+ const scrollParentElement = getScrollParent(selectToggleRef.current);
+ const scrollListener = scrollParentElement.addEventListener('scroll', () => {
+ setPosition(getMenuPosition(selectToggleRef.current));
+ });
+ const resizeListener = window.addEventListener('resize', () => {
+ setPosition(getMenuPosition(selectToggleRef.current));
+ });
+ return () => {
+ window.removeEventListener('resize', resizeListener);
+ scrollParentElement.removeEventListener('scroll', scrollListener);
+ };
+ }, [selectToggleRef]);
+
+ const top = isSearchable ? position.top + position.height + 64 : position.top + position.height;
+ const portalDiv = (
+
+ {children}
+
+ );
+
+ return createPortal(portalDiv, menuPortalTarget);
+};
+
+const Menu = ({
+ noResultsMessage,
+ noOptionsMessage,
+ filterOptions,
+ inputRef,
+ isSearchable,
+ filterValue,
+ options,
+ getItemProps,
+ getInputProps,
+ highlightedIndex,
+ selectedItem,
+ isMulti,
+ isFetching,
+ menuPortalTarget,
+ menuIsPortal,
+ selectToggleRef
+}) => {
+ const filteredOptions = isSearchable ? filterOptions(options, filterValue) : options;
+ const menuItems = (
+
+ );
+ if (menuIsPortal) {
+ return (
+
+ {isSearchable && (
+
+ )}
+
+ {menuItems}
+
+
+ );
+ }
+
+ return menuItems;
+};
+
+export default Menu;
diff --git a/packages/pf4-component-mapper/src/common/select/multi-value-container.js b/packages/pf4-component-mapper/src/common/select/multi-value-container.js
deleted file mode 100644
index 53f70ae5b..000000000
--- a/packages/pf4-component-mapper/src/common/select/multi-value-container.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-const MultiValueContainer = (props) => (
-
- {!Array.isArray(props.children) ? props.children : props.children[0]}
- {props.children[1]}
-
-);
-
-MultiValueContainer.propTypes = {
- children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)]).isRequired,
- data: PropTypes.shape({ label: PropTypes.node.isRequired }).isRequired,
- className: PropTypes.string
-};
-
-MultiValueContainer.defaultProps = {
- className: ''
-};
-
-export default MultiValueContainer;
diff --git a/packages/pf4-component-mapper/src/common/select/multi-value-remove.js b/packages/pf4-component-mapper/src/common/select/multi-value-remove.js
deleted file mode 100644
index 857c4481d..000000000
--- a/packages/pf4-component-mapper/src/common/select/multi-value-remove.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import React from 'react';
-import { components } from 'react-select';
-
-import { TimesCircleIcon } from '@patternfly/react-icons';
-
-const MultiValueRemove = (props) => (
-
-
-
-);
-
-export default MultiValueRemove;
diff --git a/packages/pf4-component-mapper/src/common/select/option.js b/packages/pf4-component-mapper/src/common/select/option.js
index 262aa98e4..1062c4fc1 100644
--- a/packages/pf4-component-mapper/src/common/select/option.js
+++ b/packages/pf4-component-mapper/src/common/select/option.js
@@ -1,53 +1,37 @@
import React from 'react';
-import { components } from 'react-select';
import PropTypes from 'prop-types';
-import { Checkbox } from '@patternfly/react-core';
import { CheckIcon } from '@patternfly/react-icons';
-const Option = (props) => (
-
- {props.selectProps && props.selectProps && props.selectProps.isCheckbox && (
- props.selectOption(props.data)}
- id={`${props.innerProps && props.innerProps.id}-checkbox`}
- />
- )}
-
- {props.isSelected && props.selectProps && !props.selectProps.isCheckbox && }
-
+const Option = ({ item, isActive, isSelected, ...props }) => (
+
+
+ {item.label}
+ {isSelected && (
+
+
+
+ )}
+
+
);
Option.propTypes = {
- isFocused: PropTypes.bool,
+ item: PropTypes.shape({
+ label: PropTypes.node,
+ isDisabled: PropTypes.bool,
+ disabled: PropTypes.bool
+ }).isRequired,
+ isActive: PropTypes.bool,
isSelected: PropTypes.bool,
- getStyles: PropTypes.func.isRequired,
- selectOption: PropTypes.func,
- cx: PropTypes.func.isRequired,
- data: PropTypes.shape({
- selected: PropTypes.bool
- }),
- innerProps: PropTypes.shape({
- id: PropTypes.string
- }),
- selectProps: PropTypes.shape({
- isCheckbox: PropTypes.bool
- }),
- isDisabled: PropTypes.bool
-};
-
-Option.defaultProps = {
- isFocused: false,
- isSelected: false,
- isDisabled: false,
- selectOption: () => undefined,
- selectProps: {
- isCheckbox: false
- },
- innerProps: {
- id: 'some-classname'
- }
+ onClick: PropTypes.func.isRequired
};
export default Option;
diff --git a/packages/pf4-component-mapper/src/common/select/select-styles.scss b/packages/pf4-component-mapper/src/common/select/select-styles.scss
index 58266d05e..ae4461911 100644
--- a/packages/pf4-component-mapper/src/common/select/select-styles.scss
+++ b/packages/pf4-component-mapper/src/common/select/select-styles.scss
@@ -3,188 +3,22 @@
100% { transform: rotate(360deg); }
}
-.ddorg__pf4-component-mapper__select {
- &.single-select {
- .ddorg__pf4-component-mapper__select__placeholder {
- margin-left: 8px;
- }
- .ddorg__pf4-component-mapper__select__input {
- margin-left: 6px;
- }
+.ddorg__pf4-component-mapper__select-loading-icon {
+ animation: spin 2s linear infinite;
+}
+
+.ddorg_pf4-component-mapper__select-portal-menu.ddorg_pf4-component-mapper__select-portal-menu-searchable {
+ &::before {
+ position: absolute;
+ bottom: -4px;
+ height: 4px;
+ left: 0;
+ right: 0;
+ background: white;
+ border-bottom-width: var(--pf-global--BorderWidth--sm);
+ border-bottom-color: var(--pf-global--BorderColor--dark-100);
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ content: "";
}
- .spinning {
- animation: spin 2s linear infinite;
- }
- .ddorg__pf4-component-mapper__select__control {
- box-shadow: none;
- cursor: pointer;
- border-radius: 0;
- border: 0;
- &::before {
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- content: "";
- border: var(--pf-global--BorderWidth--sm) solid;
- border-color: var(--pf-global--Color--light-200);
- border-bottom-color: var(--pf-global--Color--dark-100);
- }
- &:hover::before {
-
- border-bottom-color: var(--pf-global--active-color--100);
- }
-
- &.ddorg__pf4-component-mapper__select__control--is-focused::before {
- border-bottom-width: var(--pf-global--BorderWidth--md);
- border-bottom-color: var(--pf-global--active-color--100);
- }
- .ddorg__pf4-component-mapper__select__indicators {
- padding-right: 6px;
- z-index: 1;
- >button.pf-c-button.pf-m-plain {
- display: flex;
- justify-content: center;
- }
- .ddorg__pf4-component-mapper__select__indicator-separator {
- display: none;
- }
- svg:first-child {
- fill: var(--pf-global--Color--400);
-
- &:hover {
- fill: var(--pf-global--Color--dark-100);
- }
- }
- }
- }
-}
-
-/**
-* Move menu styles out of select scope to enable using it while using portaling for context menu
-* !important is used to override global styles comming from react-select
-* z-index of menu has to be > 400 to show over pf4-modal
-*/
-
-.ddorg__pf4-component-mapper__select__menu {
- cursor: pointer;
- border-radius: 0 !important;
- z-index: 1000 !important;
-}
-
-.ddorg__pf4-component-mapper__select__menu--option {
- display: flex;
- align-items: center;
- color: var(--pf-global--Color--dark-100);
-
- &.focused {
- background-color: var(--pf-global--Color--light-200);
- }
-
- svg {
- width: 0.6em;
- margin-right: 10px;
- fill: var(--pf-global--active-color--100);
- }
-
- div.pf-c-check {
- padding-left: 1rem;
- & + .ddorg__pf4-component-mapper__select__option {
- padding-left: 0;
- }
- }
-
- &.disabled {
- cursor: default;
- }
-
- &.disabled div {
- color: var(--pf-global--disabled-color--100);
- pointer-events: none;
- cursor: none;
- }
-}
-
-.ddorg__pf4-component-mapper__select__menu--option div {
- background: transparent;
- cursor: pointer;
- color: var(--pf-global--Color--300);
-}
-
-.ddorg__pf4-component-mapper__select__single-value {
-padding-left: 8px;
-}
-
-.ddorg__pf4-component-mapper__select__multivalue--container {
-display: flex;
-flex-direction: row;
-flex-wrap: nowrap;
-align-items: center;
-box-sizing: border-box;
-line-height: 24px;
-position: relative;
-margin-right: 4px;
-font-size: 12px;
-max-height: 26px;
-
-> .ddorg__pf4-component-mapper__select__multivalue--remove {
- display: flex;
-
- svg {
- fill: var(--pf-global--Color--400);
- }
-
- > :hover {
- background: transparent;
- svg {
- fill: var(--pf-global--Color--dark-100);
- }
- }
-}
-
-&::before {
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- content: "";
- border: var(--pf-global--BorderWidth--sm) solid var(--pf-global--Color--dark-200);
- border-radius: 3px;
- pointer-events: none;
-}
-}
-
-.ddorg__pf4-component-mapper__select__value--container {
- display: flex;
- padding-left: 8px;
- align-items: center;
- flex-wrap: wrap;
- max-width: calc(100% - 70px);
-
- .ddorg__pf4-component-mapper__select__multivalue--container {
- align-items: initial;
- .ddorg__pf4-component-mapper__select__multi-value__label {
- max-width: 240px;
- overflow: hidden;
- text-overflow: ellipsis;
- display: inline-block;
- }
- }
-
- .ddorg__pf4-component-mapper__select__value--container-chipgroup {
- padding: 4px 6px;
- font-size: 12px;
- background-color: var(--pf-global--BorderColor--300);
- border: var(--pf-global--BorderWidth--sm) solid var(--pf-global--BorderColor--300);
- margin: 0;
- max-height: 26px;
- &:hover{
- border-color: var(--pf-global--Color--dark-100);
- }
- > span {
- color: var(--pf-global--Color--dark-100);
- }
- }
}
diff --git a/packages/pf4-component-mapper/src/common/select/select.js b/packages/pf4-component-mapper/src/common/select/select.js
index bb0a8dfd5..0539ecee6 100644
--- a/packages/pf4-component-mapper/src/common/select/select.js
+++ b/packages/pf4-component-mapper/src/common/select/select.js
@@ -1,60 +1,221 @@
-import React from 'react';
+import React, { useRef, useState } from 'react';
import PropTypes from 'prop-types';
import DataDrivenSelect from '@data-driven-forms/common/src/select';
-import ReactSelect from 'react-select';
-import CreatableSelect from 'react-select/creatable';
+import parseInternalValue from '@data-driven-forms/common/src/select/parse-internal-value';
+import Downshift from 'downshift';
+import { CaretDownIcon, CloseIcon, CircleNotchIcon } from '@patternfly/react-icons';
+import '@patternfly/react-styles/css/components/Select/select.css';
+import '@patternfly/react-styles/css/components/Chip/chip.css';
+import '@patternfly/react-styles/css/components/ChipGroup/chip-group.css';
+import '@patternfly/react-styles/css/components/Divider/divider.css';
-import MultiValueContainer from './multi-value-container';
-import ValueContainer from './value-container';
-import MultiValueRemove from './multi-value-remove';
-import DropdownIndicator from './dropdown-indicator';
+import './select-styles.scss';
+import Menu from './menu';
import ClearIndicator from './clear-indicator';
-import Option from './option';
+import ValueContainer from './value-container';
-import './select-styles.scss';
+const itemToString = (value, isMulti, showMore, handleShowMore, handleChange) => {
+ if (!value) {
+ return '';
+ }
-const Select = ({ selectVariant, menuIsPortal, ...props }) => {
- const isSearchable = selectVariant === 'createable' || props.isSearchable;
- const simpleValue = selectVariant === 'createable' ? false : props.simpleValue;
+ if (Array.isArray(value)) {
+ if (!value || value.length === 0) {
+ return;
+ }
- const menuPortalTarget = menuIsPortal ? document.body : undefined;
+ if (isMulti) {
+ const visibleOptions = showMore ? value : value.slice(0, 3);
+ return (
+ event.stopPropagation()}>
+
+
+ );
+ }
+
+ return value.map((item) => (typeof item === 'object' ? item.label : item)).join(',');
+ }
+
+ if (typeof value === 'object') {
+ return value.label;
+ }
+
+ return value;
+};
+const filterOptions = (options, filterValue = '') => options.filter(({ label }) => label.toLowerCase().includes(filterValue.toLowerCase()));
+
+const getValue = (isMulti, option, value) => {
+ if (!isMulti || !option) {
+ return option;
+ }
+
+ const isSelected = value.find(({ value }) => value === option.value);
+ return isSelected ? value.filter(({ value }) => value !== option.value) : [...value, option];
+};
+
+const stateReducer = (state, changes, keepMenuOpen) => {
+ switch (changes.type) {
+ case Downshift.stateChangeTypes.keyDownEnter:
+ case Downshift.stateChangeTypes.clickItem:
+ return {
+ ...changes,
+ isOpen: keepMenuOpen ? state.isOpen : !state.isOpen,
+ highlightedIndex: state.highlightedIndex,
+ inputValue: state.inputValue // prevent filter value change after option click
+ };
+ case Downshift.stateChangeTypes.controlledPropUpdatedSelectedItem:
+ return {
+ ...changes,
+ inputValue: state.inputValue
+ };
+ default:
+ return changes;
+ }
+};
+
+const InternalSelect = ({
+ noResultsMessage,
+ noOptionsMessage,
+ onChange,
+ options,
+ value,
+ simpleValue,
+ placeholder,
+ isSearchable,
+ isDisabled,
+ isClearable,
+ isMulti,
+ isFetching,
+ onInputChange,
+ loadingMessage,
+ menuPortalTarget,
+ menuIsPortal,
+ ...props
+}) => {
+ const [showMore, setShowMore] = useState(false);
+ const inputRef = useRef();
+ const selectToggleRef = useRef();
+ const parsedValue = parseInternalValue(value);
+ const handleShowMore = () => setShowMore((prev) => !prev);
+ const handleChange = (option) => onChange(getValue(isMulti, option, value));
return (
- itemToString(value, isMulti, showMore, handleShowMore, handleChange)}
+ selectedItem={value || ''}
+ stateReducer={(state, changes) => stateReducer(state, changes, isMulti)}
+ onInputValueChange={(inputValue) => {
+ if (onInputChange && typeof inputValue === 'string') {
+ onInputChange(inputValue);
+ }
}}
- menuPortalTarget={menuPortalTarget}
- {...props}
- className={`ddorg__pf4-component-mapper__select${props.isMulti ? ' multi-select' : ' single-select'}`}
- classNamePrefix="ddorg__pf4-component-mapper__select"
- styles={{
- menuPortal: (provided) => ({
- ...provided,
- 'z-index': 'initial'
- })
+ >
+ {({ isOpen, inputValue, itemToString, selectedItem, clearSelection, getInputProps, getToggleButtonProps, getItemProps, highlightedIndex }) => {
+ const toggleButtonProps = getToggleButtonProps();
+ return (
+
+
+
+
+
+ {isClearable && parsedValue &&
}
+
+ {isFetching ? : }
+
+
+ {isOpen && (
+
+ )}
+
+ );
}}
- isSearchable={isSearchable}
- simpleValue={simpleValue}
- selectVariant={selectVariant}
- />
+
);
};
+InternalSelect.propTypes = {
+ onChange: PropTypes.func.isRequired,
+ options: PropTypes.arrayOf(
+ PropTypes.shape({
+ value: PropTypes.any,
+ label: PropTypes.any
+ })
+ ).isRequired,
+ value: PropTypes.any,
+ simpleValue: PropTypes.bool,
+ placeholder: PropTypes.string,
+ isSearchable: PropTypes.bool,
+ id: PropTypes.string,
+ name: PropTypes.string.isRequired,
+ isDisabled: PropTypes.bool,
+ isClearable: PropTypes.bool,
+ noResultsMessage: PropTypes.node,
+ noOptionsMessage: PropTypes.func,
+ isMulti: PropTypes.bool,
+ isFetching: PropTypes.bool,
+ onInputChange: PropTypes.func,
+ loadingMessage: PropTypes.node,
+ menuPortalTarget: PropTypes.any,
+ menuIsPortal: PropTypes.bool
+};
+
+const Select = ({ menuIsPortal, ...props }) => {
+ const menuPortalTarget = menuIsPortal ? document.body : undefined;
+
+ return ;
+};
+
Select.propTypes = {
- selectVariant: PropTypes.oneOf(['default', 'createable']),
isSearchable: PropTypes.bool,
showMoreLabel: PropTypes.node,
showLessLabel: PropTypes.node,
@@ -71,13 +232,13 @@ Select.propTypes = {
loadOptions: PropTypes.func,
loadingMessage: PropTypes.node,
updatingMessage: PropTypes.node,
- noOptionsMessage: PropTypes.func,
menuIsPortal: PropTypes.bool,
- placeholder: PropTypes.string
+ placeholder: PropTypes.string,
+ noResultsMessage: PropTypes.node,
+ noOptionsMessage: PropTypes.node
};
Select.defaultProps = {
- selectVariant: 'default',
showMoreLabel: 'more',
showLessLabel: 'Show less',
simpleValue: true,
@@ -87,7 +248,9 @@ Select.defaultProps = {
menuIsPortal: false,
placeholder: 'Choose...',
isSearchable: false,
- isClearable: false
+ isClearable: false,
+ noResultsMessage: 'No results found',
+ noOptionsMessage: 'No options'
};
export default Select;
diff --git a/packages/pf4-component-mapper/src/common/select/value-container.js b/packages/pf4-component-mapper/src/common/select/value-container.js
index 6c9c188ac..3857bd47f 100644
--- a/packages/pf4-component-mapper/src/common/select/value-container.js
+++ b/packages/pf4-component-mapper/src/common/select/value-container.js
@@ -1,53 +1,13 @@
-import React, { Component } from 'react';
+import React from 'react';
import PropTypes from 'prop-types';
-import { Button } from '@patternfly/react-core';
-
-class ValueContainer extends Component {
- state = {
- showAll: false
- };
- render() {
- const { isMulti, ...props } = this.props;
- const { showAll } = this.state;
- if (isMulti && props.children) {
- return (
-
- {showAll ? props.children[0] : props.children[0] && props.children[0][0] ? props.children[0][0] : props.children[0]}
- {props.children[0] && props.children[0].length > 1 && (
- this.setState(({ showAll }) => ({ showAll: !showAll }))}
- variant="plain"
- >
- {showAll ? props.selectProps.showLessLabel : `${props.children[0].length - 1} ${props.selectProps.showMoreLabel}`}
-
- )}
- {Array.isArray(props.children) ? props.children[1] && props.children[1] : props.children}
-
- );
- }
-
- return props.children;
- }
-}
-
-ValueContainer.propTypes = {
- isMulti: PropTypes.bool,
- getStyles: PropTypes.func.isRequired,
- children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)]).isRequired,
- selectProps: PropTypes.shape({
- showLessLabel: PropTypes.node,
- showMoreLabel: PropTypes.node
- })
+const ValueContainer = ({ value, placeholder }) => {
+ return {value || placeholder};
};
-ValueContainer.defaultProps = {
- isMulti: false,
- selectProps: {
- showLessLabel: 'Show less',
- showMoreLabel: 'more'
- }
+ValueContainer.propTypes = {
+ value: PropTypes.node,
+ placeholder: PropTypes.node
};
export default ValueContainer;
diff --git a/packages/pf4-component-mapper/src/tests/form-fields.test.js b/packages/pf4-component-mapper/src/tests/form-fields.test.js
index 557195152..e912a5a86 100644
--- a/packages/pf4-component-mapper/src/tests/form-fields.test.js
+++ b/packages/pf4-component-mapper/src/tests/form-fields.test.js
@@ -417,6 +417,8 @@ describe('FormFields', () => {
.first()
.props().disabled
).toEqual(true);
+ } else if (component === componentTypes.SELECT) {
+ expect(wrapper.find('div.pf-c-select__toggle').prop('disabled')).toEqual(true);
} else {
expect(
wrapper
@@ -434,6 +436,11 @@ describe('FormFields', () => {
};
const wrapper = mount();
+ if (component === componentTypes.SELECT) {
+ expect(true);
+ return;
+ }
+
if (component === componentTypes.TEXTAREA) {
expect(
wrapper
diff --git a/packages/pf4-component-mapper/src/tests/select/select.test.js b/packages/pf4-component-mapper/src/tests/select/select.test.js
index 8dbc0f98f..b75b66df5 100644
--- a/packages/pf4-component-mapper/src/tests/select/select.test.js
+++ b/packages/pf4-component-mapper/src/tests/select/select.test.js
@@ -1,7 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
-import { components } from 'react-select';
-import ReactSelect from 'react-select';
import isEqual from 'lodash/isEqual';
import Select from '../../common/select/select';
@@ -13,7 +11,7 @@ describe('', () => {
beforeEach(() => {
initialProps = {
onChange,
- menuIsOpen: true,
+ name: 'test-select',
id: 'select',
options: [
{
@@ -34,11 +32,8 @@ describe('', () => {
it('should return single simple value', async () => {
const wrapper = mount();
- const option = wrapper
- .find('.ddorg__pf4-component-mapper__select__menu--option')
- .first()
- .find('div')
- .last();
+ wrapper.find('.pf-c-select__toggle').simulate('click');
+ const option = wrapper.find('button.pf-c-select__menu-item').first();
await act(async () => {
option.simulate('click');
@@ -49,11 +44,8 @@ describe('', () => {
it('should return single object value', async () => {
const wrapper = mount();
- const option = wrapper
- .find('.ddorg__pf4-component-mapper__select__menu--option')
- .first()
- .find('div')
- .last();
+ wrapper.find('.pf-c-select__toggle').simulate('click');
+ const option = wrapper.find('button.pf-c-select__menu-item').first();
await act(async () => {
option.simulate('click');
@@ -67,14 +59,11 @@ describe('', () => {
// simulate first return value in state
const value = [1];
const wrapper = mount();
+ wrapper.find('.pf-c-select__toggle').simulate('click');
/**
* select first option
*/
- const option1 = wrapper
- .find('.ddorg__pf4-component-mapper__select__menu--option')
- .first()
- .find('div')
- .last();
+ const option1 = wrapper.find('button.pf-c-select__menu-item').first();
await act(async () => {
option1.simulate('click');
@@ -82,11 +71,7 @@ describe('', () => {
/**
* select second option
*/
- const option2 = wrapper
- .find('.ddorg__pf4-component-mapper__select__menu--option')
- .last()
- .find('div')
- .last();
+ const option2 = wrapper.find('button.pf-c-select__menu-item').last();
await act(async () => {
option2.simulate('click');
});
@@ -101,14 +86,11 @@ describe('', () => {
// simulate first return value in state
const value = [{ ...initialProps.options[0] }];
const wrapper = mount();
+ wrapper.find('.pf-c-select__toggle').simulate('click');
/**
* select first option
*/
- const option1 = wrapper
- .find('.ddorg__pf4-component-mapper__select__menu--option')
- .first()
- .find('div')
- .last();
+ const option1 = wrapper.find('button.pf-c-select__menu-item').first();
await act(async () => {
option1.simulate('click');
@@ -116,11 +98,7 @@ describe('', () => {
/**
* select second option
*/
- const option2 = wrapper
- .find('.ddorg__pf4-component-mapper__select__menu--option')
- .last()
- .find('div')
- .last();
+ const option2 = wrapper.find('button.pf-c-select__menu-item').last();
await act(async () => {
option2.simulate('click');
});
@@ -130,16 +108,28 @@ describe('', () => {
});
it('should expand and close multi value chips', async () => {
- const value = [1, 2];
- const wrapper = mount();
+ const value = [1, 2, 3, 4];
+ const options = [
+ ...initialProps.options,
+ {
+ label: '3',
+ value: 3
+ },
+ {
+ label: '4',
+ value: 4
+ }
+ ];
+ const wrapper = mount();
- expect(wrapper.find('.ddorg__pf4-component-mapper__select__multivalue--container')).toHaveLength(1);
- const expandButton = wrapper.find('button.pf-c-button.pf-m-plain.ddorg__pf4-component-mapper__select__value--container-chipgroup');
+ expect(wrapper.find('.pf-c-chip-group')).toHaveLength(1);
+ expect(wrapper.find('div.pf-c-chip')).toHaveLength(3);
+ const expandButton = wrapper.find('button.pf-c-chip.pf-m-overflow').last();
await act(async () => {
expandButton.simulate('click');
});
wrapper.update();
- expect(wrapper.find('.ddorg__pf4-component-mapper__select__multivalue--container')).toHaveLength(2);
+ expect(wrapper.find('div.pf-c-chip')).toHaveLength(4);
});
it('should call on change when removing chip', async () => {
@@ -148,7 +138,7 @@ describe('', () => {
await act(async () => {
wrapper
- .find(components.MultiValueRemove)
+ .find('button.pf-c-button.pf-m-plain')
.first()
.simulate('click');
});
@@ -180,19 +170,20 @@ describe('', () => {
{ label: 'b', value: 2 }
],
placeholder: 'Choose...',
- selectVariant: 'default',
showLessLabel: 'Show less',
showMoreLabel: 'more',
simpleValue: true,
updatingMessage: 'Loading data...',
menuIsPortal: false,
value: [1, 2],
- loadingMessage: 'Loading...'
+ loadingMessage: 'Loading...',
+ noOptionsMessage: 'No options',
+ noResultsMessage: 'No results found'
});
});
it('should load single select Async options correctly', async () => {
- const asyncLoading = jest.fn().mockReturnValue(Promise.resolve([{ label: 'label' }]));
+ const asyncLoading = jest.fn().mockReturnValue(Promise.resolve([{ label: 'label', value: '3' }]));
let wrapper;
@@ -200,13 +191,10 @@ describe('', () => {
wrapper = mount();
});
wrapper.update();
+ wrapper.find('.pf-c-select__toggle').simulate('click');
- expect(
- wrapper
- .find(ReactSelect)
- .first()
- .instance().props.options
- ).toEqual([{ label: 'label' }]);
+ expect(wrapper.find('button.pf-c-select__menu-item')).toHaveLength(1);
+ expect(wrapper.find('button.pf-c-select__menu-item').text()).toEqual('label');
});
it('should load multi select Async options correctly and set initial value to undefined', async () => {
@@ -229,12 +217,9 @@ describe('', () => {
});
wrapper.update();
- expect(
- wrapper
- .find(ReactSelect)
- .first()
- .instance().props.options
- ).toEqual([{ label: 'label', value: '123' }]);
+ wrapper.find('.pf-c-select__toggle').simulate('click');
+ expect(wrapper.find('button.pf-c-select__menu-item')).toHaveLength(1);
+ expect(wrapper.find('button.pf-c-select__menu-item').text()).toEqual('label');
expect(onChange).toHaveBeenCalledWith(undefined);
});
@@ -258,12 +243,9 @@ describe('', () => {
});
wrapper.update();
- expect(
- wrapper
- .find(ReactSelect)
- .first()
- .instance().props.options
- ).toEqual([{ label: 'label', value: '123' }]);
+ wrapper.find('.pf-c-select__toggle').simulate('click');
+ expect(wrapper.find('button.pf-c-select__menu-item')).toHaveLength(1);
+ expect(wrapper.find('button.pf-c-select__menu-item').text()).toEqual('label');
expect(onChange).toHaveBeenCalledWith(['123']);
});
@@ -286,17 +268,14 @@ describe('', () => {
});
wrapper.update();
- expect(
- wrapper
- .find(ReactSelect)
- .first()
- .instance().props.options
- ).toEqual([{ label: 'label', value: '123' }]);
+ wrapper.find('.pf-c-select__toggle').simulate('click');
+ expect(wrapper.find('button.pf-c-select__menu-item')).toHaveLength(1);
+ expect(wrapper.find('button.pf-c-select__menu-item').text()).toEqual('label');
expect(onChange).toHaveBeenCalledWith([{ label: 'label', value: '123' }]);
});
it('should load Async options after filtering', async () => {
- const asyncLoading = jest.fn().mockReturnValue(Promise.resolve([{ label: 'label' }]));
+ const asyncLoading = jest.fn().mockReturnValue(Promise.resolve([{ label: 'label', value: 1 }]));
let wrapper;
await act(async () => {
wrapper = mount();
@@ -304,8 +283,9 @@ describe('', () => {
wrapper.update();
expect(asyncLoading.mock.calls).toHaveLength(1);
+ wrapper.find('.pf-c-select__toggle').simulate('click');
- const search = wrapper.find(ReactSelect).find('input');
+ const search = wrapper.find('input');
await act(async () => {
search.instance().value = 'foo';
@@ -339,7 +319,7 @@ describe('', () => {
wrapper = mount();
});
- let innerSelectProps = wrapper.find(ReactSelect).props().options;
+ let innerSelectProps = wrapper.find('InternalSelect').props().options;
expect(isEqual(innerSelectProps, initialProps.options)).toEqual(true);
@@ -347,7 +327,7 @@ describe('', () => {
wrapper.setProps({ options: NEW_OPTIONS });
});
wrapper.update();
- innerSelectProps = wrapper.find(ReactSelect).props().options;
+ innerSelectProps = wrapper.find('InternalSelect').props().options;
expect(innerSelectProps).toEqual(NEW_OPTIONS);
});
@@ -359,7 +339,7 @@ describe('', () => {
});
wrapper.update();
- let innerSelectProps = wrapper.find(ReactSelect).props().options;
+ let innerSelectProps = wrapper.find('InternalSelect').props().options;
expect(isEqual(innerSelectProps, initialProps.options)).toEqual(true);
@@ -368,7 +348,7 @@ describe('', () => {
});
wrapper.update();
- innerSelectProps = wrapper.find(ReactSelect).props().options;
+ innerSelectProps = wrapper.find('InternalSelect').props().options;
expect(isEqual(innerSelectProps, NEW_OPTIONS)).toEqual(true);
});
diff --git a/yarn.lock b/yarn.lock
index fb8aaf95d..fa5b95a9f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1691,6 +1691,13 @@
dependencies:
regenerator-runtime "^0.13.4"
+"@babel/runtime@^7.9.6":
+ version "7.10.2"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.2.tgz#d103f21f2602497d38348a32e008637d506db839"
+ integrity sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
"@babel/template@^7.4.0", "@babel/template@^7.7.0":
version "7.7.0"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.7.0.tgz#4fadc1b8e734d97f56de39c77de76f2562e597d0"
@@ -3281,10 +3288,6 @@
dependencies:
"@types/node" ">= 8"
-"@patternfly/patternfly-next@^1.0.175":
- version "1.0.175"
- resolved "https://registry.yarnpkg.com/@patternfly/patternfly-next/-/patternfly-next-1.0.175.tgz#47d2ee6448ee7132874290e8bb68bbd5d52e1664"
-
"@patternfly/react-core@^4.18.5":
version "4.18.5"
resolved "https://registry.yarnpkg.com/@patternfly/react-core/-/react-core-4.18.5.tgz#465ee3be0e58f7fdead9644ed2667f18eff0d684"
@@ -6726,6 +6729,11 @@ compression@1.7.4, compression@^1.7.0, compression@^1.7.4:
safe-buffer "5.1.2"
vary "~1.1.2"
+compute-scroll-into-view@^1.0.13:
+ version "1.0.14"
+ resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.14.tgz#80e3ebb25d6aa89f42e533956cb4b16a04cfe759"
+ integrity sha512-mKDjINe3tc6hGelUMNDzuhorIUZ7kS7BwyY0r2wQd2HOH2tRuJykiC06iSEX8y1TuhNzvz4GcJnK16mM2J1NMQ==
+
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
@@ -8124,6 +8132,16 @@ dotenv@^6.1.0:
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064"
integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==
+downshift@^5.4.3:
+ version "5.4.3"
+ resolved "https://registry.yarnpkg.com/downshift/-/downshift-5.4.3.tgz#7dda5e5d07aa06d440af4e691e318ec9b4e3e78f"
+ integrity sha512-5RdEBfIfhK4dqoXvLKGrFqY+kE9iBjZJrJoxoWXQzXMCybyr7pbqq4rvXqvzbZtmCuCB8ZN9iGz5V96rv+Q7fw==
+ dependencies:
+ "@babel/runtime" "^7.9.6"
+ compute-scroll-into-view "^1.0.13"
+ prop-types "^15.7.2"
+ react-is "^16.13.1"
+
drmonty-datatables-colvis@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/drmonty-datatables-colvis/-/drmonty-datatables-colvis-1.1.2.tgz#96ab9edfb48643cc2edda3f87b88933cdee8127c"
@@ -17307,7 +17325,7 @@ react-is@16.8.6:
version "16.8.6"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
-react-is@^16.12.0, react-is@^16.3.2, react-is@^16.6.3, react-is@^16.8.6:
+react-is@^16.12.0, react-is@^16.13.1, react-is@^16.3.2, react-is@^16.6.3, react-is@^16.8.6:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@@ -17406,7 +17424,7 @@ react-redux@^7.1.1, react-redux@^7.1.3:
prop-types "^15.7.2"
react-is "^16.9.0"
-react-select@^3.0.4, react-select@^3.0.8:
+react-select@^3.0.8:
version "3.0.8"
resolved "https://registry.yarnpkg.com/react-select/-/react-select-3.0.8.tgz#06ff764e29db843bcec439ef13e196865242e0c1"
dependencies:
@@ -18162,7 +18180,7 @@ resolve@1.1.7:
version "1.1.7"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
-resolve@^1.1.7, resolve@^1.11.0, resolve@^1.11.1, resolve@^1.14.1, resolve@^1.16.0:
+resolve@^1.1.7, resolve@^1.11.0, resolve@^1.11.1, resolve@^1.14.1, resolve@^1.16.0, resolve@^1.16.1:
version "1.17.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
@@ -18341,6 +18359,26 @@ rollup-plugin-postcss@^2.0.3:
safe-identifier "^0.4.1"
style-inject "^0.3.0"
+rollup-plugin-postcss@^3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/rollup-plugin-postcss/-/rollup-plugin-postcss-3.1.2.tgz#e862033b96fabb73390fd4ccbee0155385d30e46"
+ integrity sha512-29ocL0CqjLj9sUghTG64ZwFxwbo2d0WyOTVtqPg6SEMZyFmKke9TClBf4/CcdFaWHqS+YZGsUpq3mzIBSYrw+A==
+ dependencies:
+ chalk "^4.0.0"
+ concat-with-sourcemaps "^1.1.0"
+ cssnano "^4.1.10"
+ import-cwd "^3.0.0"
+ p-queue "^6.3.0"
+ pify "^5.0.0"
+ postcss "^7.0.27"
+ postcss-load-config "^2.1.0"
+ postcss-modules "^2.0.0"
+ promise.series "^0.2.0"
+ resolve "^1.16.1"
+ rollup-pluginutils "^2.8.2"
+ safe-identifier "^0.4.1"
+ style-inject "^0.3.0"
+
rollup-plugin-replace@^2.1.0, rollup-plugin-replace@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/rollup-plugin-replace/-/rollup-plugin-replace-2.2.0.tgz#f41ae5372e11e7a217cde349c8b5d5fd115e70e3"