diff --git a/assets/index.less b/assets/index.less index fddf02b28..ad35abae7 100644 --- a/assets/index.less +++ b/assets/index.less @@ -2,6 +2,23 @@ @background-color: rgb(255, 240, 255); +@input-placeholder-color: hsv(0, 0, 75%); + +.placeholder(@color: @input-placeholder-color) { + // Firefox + &::-moz-placeholder { + opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526 + } + + &::placeholder { + color: @color; + } + + &:placeholder-shown { + text-overflow: ellipsis; + } +} + .@{prefix-cls} { display: inline-flex; @@ -292,6 +309,13 @@ > input { width: 100%; + .placeholder(); + } + + &-placeholder { + > input { + color: @input-placeholder-color; + } } } diff --git a/src/Picker.tsx b/src/Picker.tsx index 5873b1352..b4850ba54 100644 --- a/src/Picker.tsx +++ b/src/Picker.tsx @@ -30,7 +30,7 @@ import { getDefaultFormat, getInputSize, elementsContains } from './utils/uiUtil import usePickerInput from './hooks/usePickerInput'; import useTextValueMapping from './hooks/useTextValueMapping'; import useValueTexts from './hooks/useValueTexts'; -import useHoverPlaceholder from './hooks/useHoverPlaceholder'; +import useHoverValue from './hooks/useHoverValue'; export interface PickerRefConfig { focus: () => void; @@ -436,7 +436,7 @@ function InnerPicker(props: PickerProps) { }; const popupPlacement = direction === 'rtl' ? 'bottomRight' : 'bottomLeft'; - const [hoverPlaceholder, onEnter, onLeave] = useHoverPlaceholder(placeholder, text, { + const [hoverValue, onEnter, onLeave] = useHoverValue(text, { formatList, generateConfig, locale, @@ -481,18 +481,23 @@ function InnerPicker(props: PickerProps) { onContextMenu={onContextMenu} onClick={onClick} > -
+
{ triggerTextChange(e.target.value); }} autoFocus={autoFocus} - placeholder={hoverPlaceholder} + placeholder={placeholder} ref={inputRef} title={text} {...inputProps} diff --git a/src/RangePicker.tsx b/src/RangePicker.tsx index 31ba3e125..d985dd362 100644 --- a/src/RangePicker.tsx +++ b/src/RangePicker.tsx @@ -29,7 +29,7 @@ import getExtraFooter from './utils/getExtraFooter'; import getRanges from './utils/getRanges'; import useRangeViewDates from './hooks/useRangeViewDates'; import { DateRender } from './panels/DatePanel/DateBody'; -import useHoverPlaceholder from './hooks/useHoverPlaceholder'; +import useHoverValue from './hooks/useHoverValue'; function reorderValues( values: RangeValue, @@ -540,25 +540,17 @@ function InnerRangePicker(props: RangePickerProps) { // ========================== Hover Range ========================== const [hoverRangedValue, setHoverRangedValue] = useState>(null); - const [startPlaceholder, onStartEnter, onStartLeave] = useHoverPlaceholder( - getValue(placeholder, 0) || '', - startText, - { - formatList, - generateConfig, - locale, - }, - ); + const [startHoverValue, onStartEnter, onStartLeave] = useHoverValue(startText, { + formatList, + generateConfig, + locale, + }); - const [endPlaceholder, onEndEnter, onEndLeave] = useHoverPlaceholder( - getValue(placeholder, 1) || '', - endText, - { - formatList, - generateConfig, - locale, - }, - ); + const [endHoverValue, onEndEnter, onEndLeave] = useHoverValue(endText, { + formatList, + generateConfig, + locale, + }); const onDateMouseEnter = (date: DateType) => { setHoverRangedValue(updateValues(selectedValue, date, mergedActivePickerIndex)); @@ -1060,6 +1052,7 @@ function InnerRangePicker(props: RangePickerProps) {
@@ -1067,12 +1060,12 @@ function InnerRangePicker(props: RangePickerProps) { id={id} disabled={mergedDisabled[0]} readOnly={inputReadOnly || !startTyping} - value={startText} + value={startHoverValue || startText} onChange={e => { triggerStartTextChange(e.target.value); }} autoFocus={autoFocus} - placeholder={startPlaceholder} + placeholder={getValue(placeholder, 0) || ''} ref={startInputRef} {...startInputProps} {...inputSharedProps} @@ -1085,17 +1078,18 @@ function InnerRangePicker(props: RangePickerProps) {
{ triggerEndTextChange(e.target.value); }} - placeholder={endPlaceholder} + placeholder={getValue(placeholder, 1) || ''} ref={endInputRef} {...endInputProps} {...inputSharedProps} diff --git a/src/hooks/useHoverPlaceholder.ts b/src/hooks/useHoverValue.ts similarity index 57% rename from src/hooks/useHoverPlaceholder.ts rename to src/hooks/useHoverValue.ts index cd2fcff60..b4135f9a0 100644 --- a/src/hooks/useHoverPlaceholder.ts +++ b/src/hooks/useHoverValue.ts @@ -1,28 +1,29 @@ -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import useValueTexts, { ValueTextConfig } from './useValueTexts'; -export default function useHoverPlaceholder( - placeholder: string, - text: string, +export default function useHoverValue( + valueText: string, { formatList, generateConfig, locale }: ValueTextConfig, ): [string, (date: DateType) => void, (date: DateType) => void] { const [value, setValue] = useState(null); - const [valueTexts] = useValueTexts(value, { + const [, firstText] = useValueTexts(value, { formatList, generateConfig, locale, }); function onEnter(date: DateType) { - if (!text) { - setValue(date); - } + setValue(date); } function onLeave() { setValue(null); } - return [(valueTexts && valueTexts[0]) || placeholder, onEnter, onLeave]; + useEffect(() => { + onLeave(); + }, [valueText]); + + return [firstText, onEnter, onLeave]; } diff --git a/tests/picker.spec.tsx b/tests/picker.spec.tsx index 4544b4125..5eb625a80 100644 --- a/tests/picker.spec.tsx +++ b/tests/picker.spec.tsx @@ -730,33 +730,52 @@ describe('Picker.Basic', () => { expect(wrapper.render()).toMatchSnapshot(); }); - describe('hover placeholder', () => { - const placeholder = 'custom placeholder'; - it('placeholder should be origin value when input value is not empty', () => { - const wrapper = mount( - , - ); + describe('hover value', () => { + it('should restore when leave', () => { + const wrapper = mount(); const cell = wrapper.findCell(24); cell.simulate('mouseEnter'); - expect(wrapper.find('input').prop('placeholder')).toBe(placeholder); + expect(wrapper.find('input').prop('value')).toBe('2020-07-24'); + expect(wrapper.find('.rc-picker-input').hasClass('rc-picker-input-placeholder')).toBeTruthy(); + + cell.simulate('mouseLeave'); + expect(wrapper.find('input').prop('value')).toBe('2020-07-22'); + expect(wrapper.find('.rc-picker-input').hasClass('rc-picker-input-placeholder')).toBeFalsy(); }); - it('placeholder should be target date when input value is empty', () => { - const wrapper = mount( - , - ); + it('should restore after selecting cell', () => { + const wrapper = mount(); + wrapper.openPicker(); + const cell = wrapper.findCell(24); + cell.simulate('mouseEnter'); + expect(wrapper.find('input').prop('value')).toBe('2020-07-24'); + expect(wrapper.find('.rc-picker-input').hasClass('rc-picker-input-placeholder')).toBeTruthy(); + + wrapper.selectCell(24); + expect(wrapper.find('input').prop('value')).toBe('2020-07-24'); + expect(wrapper.find('.rc-picker-input').hasClass('rc-picker-input-placeholder')).toBeFalsy(); + }); + + it('change value when hovering', () => { + const wrapper = mount(); wrapper.openPicker(); + const cell = wrapper.findCell(24); + cell.simulate('mouseEnter'); + expect(wrapper.find('input').prop('value')).toBe('2020-07-24'); + expect(wrapper.find('.rc-picker-input').hasClass('rc-picker-input-placeholder')).toBeTruthy(); + wrapper.find('input').simulate('change', { target: { - value: '', + value: '2020-07-23', }, }); - const cell = wrapper.findCell(24); - cell.simulate('mouseEnter'); - expect(wrapper.find('input').prop('placeholder')).toBe('2020-07-24'); - cell.simulate('mouseLeave'); - expect(wrapper.find('input').prop('placeholder')).toBe(placeholder); + + expect(wrapper.find('input').prop('value')).toBe('2020-07-23'); + expect(wrapper.find('.rc-picker-input').hasClass('rc-picker-input-placeholder')).toBeFalsy(); + wrapper.closePicker(); + expect(wrapper.find('input').prop('value')).toBe('2020-07-23'); + expect(wrapper.find('.rc-picker-input').hasClass('rc-picker-input-placeholder')).toBeFalsy(); }); }); }); diff --git a/tests/range.spec.tsx b/tests/range.spec.tsx index 6ca193e76..da94d5f8e 100644 --- a/tests/range.spec.tsx +++ b/tests/range.spec.tsx @@ -1428,77 +1428,236 @@ describe('Picker.Range', () => { }); describe('hover placeholder', () => { - const placeholder: [string, string] = ['custom placeholder1', 'custom placeholder2']; const defaultValue: [Moment, Moment] = [getMoment('2020-07-22'), getMoment('2020-08-22')]; - it('placeholder should be target date when input value is empty', () => { - const wrapper = mount( - , - ); - wrapper.openPicker(0); - wrapper.inputValue(''); + it('should restore when leave', () => { + const wrapper = mount(); + // left - const leftCell = wrapper.findCell('24'); + wrapper.openPicker(0); + const leftCell = wrapper.findCell(24); leftCell.simulate('mouseEnter'); expect( wrapper .find('input') .first() - .prop('placeholder'), + .prop('value'), ).toBe('2020-07-24'); expect( wrapper .find('input') .last() - .prop('placeholder'), - ).toBe(placeholder[1]); + .prop('value'), + ).toBe('2020-08-22'); + expect( + wrapper + .find('.rc-picker-input') + .first() + .hasClass('rc-picker-input-placeholder'), + ).toBeTruthy(); + expect( + wrapper + .find('.rc-picker-input') + .last() + .hasClass('rc-picker-input-placeholder'), + ).toBeFalsy(); + leftCell.simulate('mouseLeave'); expect( wrapper .find('input') .first() - .prop('placeholder'), - ).toBe(placeholder[0]); + .prop('value'), + ).toBe('2020-07-22'); expect( wrapper .find('input') .last() - .prop('placeholder'), - ).toBe(placeholder[1]); + .prop('value'), + ).toBe('2020-08-22'); + expect( + wrapper + .find('.rc-picker-input') + .first() + .hasClass('rc-picker-input-placeholder'), + ).toBeFalsy(); + expect( + wrapper + .find('.rc-picker-input') + .last() + .hasClass('rc-picker-input-placeholder'), + ).toBeFalsy(); + wrapper.closePicker(0); // right wrapper.openPicker(1); - wrapper.inputValue('', 1); - - const rightCell = wrapper.findCell('24', 1); + const rightCell = wrapper.findCell(24, 1); rightCell.simulate('mouseEnter'); + expect( + wrapper + .find('input') + .first() + .prop('value'), + ).toBe('2020-07-22'); expect( wrapper .find('input') .last() - .prop('placeholder'), + .prop('value'), ).toBe('2020-08-24'); expect( wrapper - .find('input') + .find('.rc-picker-input') .first() - .prop('placeholder'), - ).toBe(placeholder[0]); + .hasClass('rc-picker-input-placeholder'), + ).toBeFalsy(); + expect( + wrapper + .find('.rc-picker-input') + .last() + .hasClass('rc-picker-input-placeholder'), + ).toBeTruthy(); + rightCell.simulate('mouseLeave'); expect( wrapper .find('input') .first() - .prop('placeholder'), - ).toBe(placeholder[0]); + .prop('value'), + ).toBe('2020-07-22'); expect( wrapper .find('input') .last() - .prop('placeholder'), - ).toBe(placeholder[1]); + .prop('value'), + ).toBe('2020-08-22'); + expect( + wrapper + .find('.rc-picker-input') + .first() + .hasClass('rc-picker-input-placeholder'), + ).toBeFalsy(); + expect( + wrapper + .find('.rc-picker-input') + .last() + .hasClass('rc-picker-input-placeholder'), + ).toBeFalsy(); + wrapper.closePicker(1); }); + + it('should restore after selecting cell', () => { + const wrapper = mount(); + // left + wrapper.openPicker(0); + const leftCell = wrapper.findCell(24, 0); + leftCell.simulate('mouseEnter'); + expect( + wrapper + .find('input') + .first() + .prop('value'), + ).toBe('2020-07-24'); + expect( + wrapper + .find('input') + .last() + .prop('value'), + ).toBe('2020-08-22'); + expect( + wrapper + .find('.rc-picker-input') + .first() + .hasClass('rc-picker-input-placeholder'), + ).toBeTruthy(); + expect( + wrapper + .find('.rc-picker-input') + .last() + .hasClass('rc-picker-input-placeholder'), + ).toBeFalsy(); + + wrapper.selectCell(24, 0); + expect( + wrapper + .find('input') + .first() + .prop('value'), + ).toBe('2020-07-24'); + expect( + wrapper + .find('input') + .last() + .prop('value'), + ).toBe('2020-08-22'); + expect( + wrapper + .find('.rc-picker-input') + .first() + .hasClass('rc-picker-input-placeholder'), + ).toBeFalsy(); + expect( + wrapper + .find('.rc-picker-input') + .last() + .hasClass('rc-picker-input-placeholder'), + ).toBeFalsy(); + + // right + const rightCell = wrapper.findCell(24, 1); + rightCell.simulate('mouseEnter'); + expect( + wrapper + .find('input') + .first() + .prop('value'), + ).toBe('2020-07-24'); + expect( + wrapper + .find('input') + .last() + .prop('value'), + ).toBe('2020-08-24'); + expect( + wrapper + .find('.rc-picker-input') + .first() + .hasClass('rc-picker-input-placeholder'), + ).toBeFalsy(); + expect( + wrapper + .find('.rc-picker-input') + .last() + .hasClass('rc-picker-input-placeholder'), + ).toBeTruthy(); + + wrapper.selectCell(24, 1); + expect( + wrapper + .find('input') + .first() + .prop('value'), + ).toBe('2020-07-24'); + expect( + wrapper + .find('input') + .last() + .prop('value'), + ).toBe('2020-08-24'); + expect( + wrapper + .find('.rc-picker-input') + .first() + .hasClass('rc-picker-input-placeholder'), + ).toBeFalsy(); + expect( + wrapper + .find('.rc-picker-input') + .last() + .hasClass('rc-picker-input-placeholder'), + ).toBeFalsy(); + }); }); });