diff --git a/superset/assets/spec/javascripts/components/OnPasteSelect_spec.jsx b/superset/assets/spec/javascripts/components/OnPasteSelect_spec.jsx index 757614a3a08..d7e2635bf5e 100644 --- a/superset/assets/spec/javascripts/components/OnPasteSelect_spec.jsx +++ b/superset/assets/spec/javascripts/components/OnPasteSelect_spec.jsx @@ -74,7 +74,7 @@ describe('OnPasteSelect', () => { }); describe('onPaste', () => { - it('calls onChange with pasted values', () => { + it('calls onChange with pasted comma separated values', () => { wrapper.instance().onPaste(evt); expected = props.options.slice(0, 4); expect(props.onChange.calledWith(expected)).toBe(true); @@ -82,7 +82,39 @@ describe('OnPasteSelect', () => { expect(props.isValidNewOption.callCount).toBe(5); }); - it('calls onChange without any duplicate values and adds new values', () => { + it('calls onChange with pasted new line separated values', () => { + evt.clipboardData.getData = sinon.spy(() => + 'United States\nChina\nRussian Federation\nIndia', + ); + wrapper.instance().onPaste(evt); + expected = [ + props.options[0], + props.options[1], + props.options[4], + props.options[2], + ]; + expect(props.onChange.calledWith(expected)).toBe(true); + expect(evt.preventDefault.called).toBe(true); + expect(props.isValidNewOption.callCount).toBe(9); + }); + + it('calls onChange with pasted tab separated values', () => { + evt.clipboardData.getData = sinon.spy(() => + 'Russian Federation\tMexico\tIndia\tCanada', + ); + wrapper.instance().onPaste(evt); + expected = [ + props.options[4], + props.options[6], + props.options[2], + props.options[3], + ]; + expect(props.onChange.calledWith(expected)).toBe(true); + expect(evt.preventDefault.called).toBe(true); + expect(props.isValidNewOption.callCount).toBe(13); + }); + + it('calls onChange without duplicate values and adds new comma separated values', () => { evt.clipboardData.getData = sinon.spy(() => 'China, China, China, China, Mexico, Mexico, Chi na, Mexico, ', ); @@ -94,12 +126,43 @@ describe('OnPasteSelect', () => { wrapper.instance().onPaste(evt); expect(props.onChange.calledWith(expected)).toBe(true); expect(evt.preventDefault.called).toBe(true); - expect(props.isValidNewOption.callCount).toBe(9); + expect(props.isValidNewOption.callCount).toBe(17); expect(props.options[0].value).toBe(expected[2].value); props.options.splice(0, 1); }); - it('calls onChange with currently selected values and new values', () => { + it('calls onChange without duplicate values and parses new line separated values', () => { + evt.clipboardData.getData = sinon.spy(() => + 'United States\nCanada\nMexico\nUnited States\nCanada', + ); + expected = [ + props.options[0], + props.options[3], + props.options[6], + ]; + wrapper.instance().onPaste(evt); + expect(props.onChange.calledWith(expected)).toBe(true); + expect(evt.preventDefault.called).toBe(true); + expect(props.isValidNewOption.callCount).toBe(20); + }); + + it('calls onChange without duplicate values and parses tab separated values', () => { + evt.clipboardData.getData = sinon.spy(() => + 'China\tIndia\tChina\tRussian Federation\tJapan\tJapan', + ); + expected = [ + props.options[1], + props.options[2], + props.options[4], + props.options[5], + ]; + wrapper.instance().onPaste(evt); + expect(props.onChange.calledWith(expected)).toBe(true); + expect(evt.preventDefault.called).toBe(true); + expect(props.isValidNewOption.callCount).toBe(24); + }); + + it('calls onChange with currently selected values and new comma separated values', () => { props.value = ['United States', 'Canada', 'Mexico']; evt.clipboardData.getData = sinon.spy(() => 'United States, Canada, Japan, India', @@ -115,7 +178,45 @@ describe('OnPasteSelect', () => { wrapper.instance().onPaste(evt); expect(props.onChange.calledWith(expected)).toBe(true); expect(evt.preventDefault.called).toBe(true); - expect(props.isValidNewOption.callCount).toBe(11); + expect(props.isValidNewOption.callCount).toBe(26); }); + + it('calls onChange with currently selected values and new "new line" separated values', () => { + props.value = ['China', 'India', 'Japan']; + evt.clipboardData.getData = sinon.spy(() => + 'Mexico\nJapan\nIndia', + ); + wrapper = shallow(); + expected = [ + props.options[1], + props.options[2], + props.options[5], + props.options[6], + ]; + wrapper.instance().onPaste(evt); + expect(props.onChange.calledWith(expected)).toBe(true); + expect(evt.preventDefault.called).toBe(true); + expect(props.isValidNewOption.callCount).toBe(27); + }); + + it('calls onChange with currently selected values and new tab separated values', () => { + props.value = ['United States', 'Canada', 'Mexico', 'Russian Federation']; + evt.clipboardData.getData = sinon.spy(() => + 'United States\tCanada\tJapan\tIndia', + ); + wrapper = shallow(); + expected = [ + props.options[0], + props.options[3], + props.options[6], + props.options[4], + props.options[5], + props.options[2], + ]; + wrapper.instance().onPaste(evt); + expect(props.onChange.calledWith(expected)).toBe(true); + expect(evt.preventDefault.called).toBe(true); + expect(props.isValidNewOption.callCount).toBe(29); + }); }); }); diff --git a/superset/assets/src/components/OnPasteSelect.jsx b/superset/assets/src/components/OnPasteSelect.jsx index acdd3f00ef7..955c922253a 100644 --- a/superset/assets/src/components/OnPasteSelect.jsx +++ b/superset/assets/src/components/OnPasteSelect.jsx @@ -84,7 +84,7 @@ export default class OnPasteSelect extends React.Component { } OnPasteSelect.propTypes = { - separator: PropTypes.string.isRequired, + separator: PropTypes.array.isRequired, selectWrap: PropTypes.func.isRequired, refFunc: PropTypes.func, onChange: PropTypes.func.isRequired, @@ -96,7 +96,7 @@ OnPasteSelect.propTypes = { isValidNewOption: PropTypes.func, }; OnPasteSelect.defaultProps = { - separator: ',', + separator: [',', '\n', '\t'], selectWrap: Select, valueKey: 'value', labelKey: 'label',