diff --git a/superset-frontend/spec/javascripts/explore/components/SelectControl_spec.jsx b/superset-frontend/spec/javascripts/explore/components/SelectControl_spec.jsx index 5409da24ff9..76ca69411a1 100644 --- a/superset-frontend/spec/javascripts/explore/components/SelectControl_spec.jsx +++ b/superset-frontend/spec/javascripts/explore/components/SelectControl_spec.jsx @@ -19,7 +19,7 @@ /* eslint-disable no-unused-expressions */ import React from 'react'; import sinon from 'sinon'; -import { shallow } from 'enzyme'; +import { shallow, mount } from 'enzyme'; import { Select, CreatableSelect } from 'src/components/Select'; import OnPasteSelect from 'src/components/Select/OnPasteSelect'; import SelectControl from 'src/explore/components/controls/SelectControl'; @@ -47,25 +47,6 @@ describe('SelectControl', () => { wrapper = shallow(); }); - it('renders with Select by default', () => { - expect(wrapper.find(OnPasteSelect)).not.toExist(); - expect(wrapper.findWhere(x => x.type() === Select)).toHaveLength(1); - }); - - it('renders with OnPasteSelect when multi', () => { - wrapper.setProps({ multi: true }); - expect(wrapper.find(OnPasteSelect)).toExist(); - expect(wrapper.findWhere(x => x.type() === Select)).toHaveLength(0); - }); - - it('renders with Creatable when freeForm', () => { - wrapper.setProps({ freeForm: true }); - expect(wrapper.find(OnPasteSelect)).not.toExist(); - expect(wrapper.findWhere(x => x.type() === CreatableSelect)).toHaveLength( - 1, - ); - }); - it('uses Select in onPasteSelect when freeForm=false', () => { wrapper = shallow(); const select = wrapper.find(OnPasteSelect); @@ -100,6 +81,52 @@ describe('SelectControl', () => { expect(selectAllProps.onChange.calledWith(expectedValues)).toBe(true); }); + describe('render', () => { + it('renders with Select by default', () => { + expect(wrapper.find(OnPasteSelect)).not.toExist(); + expect(wrapper.findWhere(x => x.type() === Select)).toHaveLength(1); + }); + + it('renders with OnPasteSelect when multi', () => { + wrapper.setProps({ multi: true }); + expect(wrapper.find(OnPasteSelect)).toExist(); + expect(wrapper.findWhere(x => x.type() === Select)).toHaveLength(0); + }); + + it('renders with Creatable when freeForm', () => { + wrapper.setProps({ freeForm: true }); + expect(wrapper.find(OnPasteSelect)).not.toExist(); + expect(wrapper.findWhere(x => x.type() === CreatableSelect)).toHaveLength( + 1, + ); + }); + describe('when select is multi', () => { + it('renders the placeholder when a selection has been made', () => { + wrapper = mount( + , + ); + expect(wrapper.html()).toContain('add something'); + }); + }); + describe('when select is single', () => { + it('does not render the placeholder when a selection has been made', () => { + wrapper = mount( + , + ); + expect(wrapper.html()).not.toContain('add something'); + }); + }); + }); + describe('getOptions', () => { it('returns the correct options', () => { wrapper.setProps(defaultProps); diff --git a/superset-frontend/src/components/Select/styles.tsx b/superset-frontend/src/components/Select/styles.tsx index e5f844ab142..73436f74837 100644 --- a/superset-frontend/src/components/Select/styles.tsx +++ b/superset-frontend/src/components/Select/styles.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import React, { CSSProperties } from 'react'; +import React, { CSSProperties, ComponentType } from 'react'; import { css, SerializedStyles, ClassNames } from '@emotion/core'; import { supersetTheme } from '@superset-ui/core'; import { @@ -241,9 +241,39 @@ export const DEFAULT_STYLES: PartialStylesConfig = { }), }; -const { ClearIndicator, DropdownIndicator, Option } = defaultComponents; +const multiInputSpanStyle = css` + color: '#879399', + position: 'absolute', + top: '2px', + left: '4px', + width: '100vw', +`; -export const DEFAULT_COMPONENTS: SelectComponentsConfig = { +const { ClearIndicator, DropdownIndicator, Option, Input } = defaultComponents; + +type SelectComponentsType = SelectComponentsConfig & { + Input: ComponentType; +}; + +// react-select is missing selectProps from their props type +// so overwriting it here to avoid errors +type InputProps = { + selectProps: { + isMulti: boolean; + value: { + length: number; + }; + placeholder: string; + }; + cx: (a: string | null, b: any, c: string) => string | void; + innerRef: (element: React.Ref) => void; + isHidden: boolean; + isDisabled?: boolean | undefined; + className?: string | undefined; + getStyles: any; +}; + +export const DEFAULT_COMPONENTS: SelectComponentsType = { Option: ({ children, innerProps, data, ...props }) => ( {({ css }) => ( @@ -272,6 +302,14 @@ export const DEFAULT_COMPONENTS: SelectComponentsConfig = { /> ), + Input: (props: InputProps) => ( +
+ + {!!(props.selectProps.isMulti && props.selectProps.value.length) && ( + {props.selectProps.placeholder} + )} +
+ ), }; export const VALUE_LABELED_STYLES: PartialStylesConfig = { diff --git a/superset-frontend/src/explore/components/controls/AdhocFilterControl.jsx b/superset-frontend/src/explore/components/controls/AdhocFilterControl.jsx index 13f8d87a5fa..e0a577920e2 100644 --- a/superset-frontend/src/explore/components/controls/AdhocFilterControl.jsx +++ b/superset-frontend/src/explore/components/controls/AdhocFilterControl.jsx @@ -267,7 +267,7 @@ export default class AdhocFilterControl extends React.Component {