diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9e4e9a6..acdb4ca 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
## 2.3.1
* Render CustomCompoundInput, InputTable, and RepeatingInput as a div instead of a fieldset when they have no legend, resolving fieldset missing legend accessibility errors
+* Update DateInput to use the native input `type="date"`
## 2.3.0
diff --git a/package.json b/package.json
index 6da5926..54eefc2 100644
--- a/package.json
+++ b/package.json
@@ -47,7 +47,6 @@
"moment": "^2.24.0",
"prop-types": "^15.6.2",
"react": "^16.14.0",
- "react-calendar": "^3.4.0",
"react-dom": "^16.14.0",
"react-quill": "^1.3.1",
"sugar-date": "^2.0.4"
diff --git a/src/components/DateInput.jsx b/src/components/DateInput.jsx
index c6f9d4e..edc9ecd 100644
--- a/src/components/DateInput.jsx
+++ b/src/components/DateInput.jsx
@@ -1,309 +1,86 @@
-import React, { Component } from 'react';
+import React from 'react';
import PropTypes from 'prop-types';
-import Calendar from 'react-calendar';
-import BaseDropdownInput from './DropdownInput';
import LineInput from './LineInput';
-import changeable from '../enhancers/changeable';
-import committable from '../enhancers/committable';
-import { getPath } from '../helpers/pathHelpers';
-import styles from '../../styles/cspace-input/DateInput.css';
-// eslint-disable-next-line import/no-webpack-loader-syntax, import/no-unresolved
-import '!style-loader!css-loader!../../styles/react-calendar/calendar.css';
-
-import {
- formatDate,
- normalizeDateString,
- normalizeISO8601DateString,
- parseNormalizedDate,
-} from '../helpers/dateHelpers';
-
-const DropdownInput = committable(changeable(BaseDropdownInput));
+import { normalizeDateString } from '../helpers/dateHelpers';
+import { getPath, pathPropType } from '../helpers/pathHelpers';
+import styles from '../../styles/cspace-input/LineInput.css';
const propTypes = {
- // TODO: Stop using propTypes in isInput, and in render method of cspace-ui Field component.
- // Until then, propTypes need to be hoisted from the base component.
- // eslint-disable-next-line react/forbid-foreign-prop-types
- ...BaseDropdownInput.propTypes,
+ name: PropTypes.string,
+ parentPath: pathPropType,
+ subpath: pathPropType,
+ value: PropTypes.string,
locale: PropTypes.string,
- tooltip: PropTypes.string,
onCommit: PropTypes.func,
+ asText: PropTypes.bool,
readOnly: PropTypes.bool,
+ embedded: PropTypes.bool,
};
const defaultProps = {
+ name: undefined,
+ parentPath: undefined,
+ subpath: undefined,
+ value: undefined,
locale: 'en-US',
- tooltip: '',
onCommit: undefined,
+ asText: undefined,
readOnly: undefined,
+ embedded: undefined,
};
-export default class DateInput extends Component {
- constructor(props) {
- super(props);
-
- this.focusCalendar = this.focusCalendar.bind(this);
- this.handleCalendarChange = this.handleCalendarChange.bind(this);
- this.handleCalendarContainerRef = this.handleCalendarContainerRef.bind(this);
- // this.handleCalendarRef = this.handleCalendarRef.bind(this);
- this.handleDropdownInputApi = this.handleDropdownInputApi.bind(this);
- this.handleDropdownInputBeforeClose = this.handleDropdownInputBeforeClose.bind(this);
- this.handleDropdownInputChange = this.handleDropdownInputChange.bind(this);
- this.handleDropdownInputClose = this.handleDropdownInputClose.bind(this);
- this.handleDropdownInputKeyDown = this.handleDropdownInputKeyDown.bind(this);
- this.handleDropdownInputMount = this.handleDropdownInputMount.bind(this);
- this.handleDropdownInputOpen = this.handleDropdownInputOpen.bind(this);
-
- const value = normalizeISO8601DateString(props.value) || props.value;
-
- this.state = {
- value,
- date: parseNormalizedDate(value),
- open: false,
- };
- }
-
- // eslint-disable-next-line camelcase
- UNSAFE_componentWillReceiveProps(nextProps) {
- const nextValue = normalizeISO8601DateString(nextProps.value) || nextProps.value;
-
- this.setState({
- value: nextValue,
- date: parseNormalizedDate(nextValue),
- });
- }
-
- handleCalendarChange(date) {
- this.setState({
- value: formatDate(date),
- open: false,
- });
-
- this.commit(date);
- this.focusInput();
- }
-
- // handleCalendarRef(ref) {
- // this.calendar = ref;
- // }
-
- handleCalendarContainerRef(ref) {
- this.calendarContainerDomNode = ref;
- }
-
- handleDropdownInputApi(api) {
- this.dropdownInputApi = api;
- }
-
- handleDropdownInputBeforeClose(isCancelled) {
- if (isCancelled) {
- const {
- value,
- } = this.props;
-
- this.setState({
- provisionalDate: undefined,
- value: normalizeISO8601DateString(value),
- });
+export default function DateInput(props) {
+ const {
+ value,
+ name,
+ onCommit,
+ asText,
+ embedded,
+ locale,
+ readOnly,
+ parentPath,
+ subpath,
+ ...remainingProps
+ } = props;
+
+ const handleChange = (event) => {
+ const eventValue = event.target.value;
+
+ const initialValue = normalizeDateString(value, locale) || '';
+ if (onCommit && eventValue !== initialValue) {
+ onCommit(getPath(props), eventValue);
}
- }
+ };
- handleDropdownInputChange(value) {
+ const className = embedded ? styles.embedded : styles.normal;
+ const normalizedValue = normalizeDateString(value, locale) || '';
+ if (asText || readOnly) {
const {
- locale,
- } = this.props;
-
- const date = parseNormalizedDate(normalizeDateString(value, locale));
-
- this.setState({
- value,
- provisionalDate: date,
- open: true,
- });
- }
-
- handleDropdownInputClose() {
- const nextState = {
- provisionalDate: undefined,
- open: false,
- };
-
- const {
- provisionalDate,
- value,
- } = this.state;
-
- if (typeof provisionalDate !== 'undefined' && value === '') {
- // Normally enter must be pressed on the provisional value in order to select a matching
- // date, but make an exception for the blank value. This allows fields to be cleared
- // without ever pressing enter. This is required by DRYD-227.
-
- nextState.value = '';
-
- const {
- onCommit,
- } = this.props;
-
- if (onCommit) {
- onCommit(getPath(this.props), '');
- }
- } else {
- const {
- value: origValue,
- } = this.props;
-
- nextState.value = normalizeISO8601DateString(origValue) || origValue;
- }
-
- this.setState(nextState);
- }
-
- handleDropdownInputKeyDown(event) {
- const {
- provisionalDate,
- value,
- } = this.state;
-
- if (event.key === 'Tab') {
- // DRYD-264: Close the dropdown when tab is depressed. The calendar is still keyboard
- // accessible by pressing down arrow. Need to close immediately (synchronously) -- otherwise
- // the calendar will receive focus before closing, leaving nothing focused. To do this, the
- // close() method of the dropdown needs to be called, rather than just passing open={false}
- // as a prop.
-
- this.dropdownInputApi.close();
-
- this.setState({
- open: false,
- });
- } else if (typeof provisionalDate !== 'undefined' && event.key === 'Enter') {
- event.preventDefault();
-
- if (provisionalDate !== null || value === '') {
- this.setState({
- open: false,
- });
-
- this.commit(provisionalDate);
- }
- }
- }
-
- handleDropdownInputMount({ focusInput }) {
- this.focusInput = focusInput;
- }
-
- handleDropdownInputOpen() {
- const {
- open,
- } = this.state;
-
- if (!open) {
- this.setState({
- open: true,
- provisionalDate: undefined,
- });
- }
- }
-
- commit(date) {
- const {
- onCommit,
- } = this.props;
-
- const {
- value: initialValue,
- } = this.props;
-
- const normalizedInitialValue = normalizeISO8601DateString(initialValue);
- const nextValue = formatDate(date);
-
- if (
- onCommit
- && (nextValue || normalizedInitialValue)
- && (nextValue !== normalizedInitialValue)
- ) {
- onCommit(getPath(this.props), nextValue);
- }
- }
-
- focusCalendar() {
- // TODO: react-calendar v6 no longer has the focus method. Maybe it will come back?
-
- // if (this.calendar) {
- // this.calendar.focus();
- // }
-
- // For now do a bit of a DOM hack to focus.
-
- if (this.calendarContainerDomNode) {
- const button = this.calendarContainerDomNode.querySelector('button');
-
- if (button) {
- button.focus();
- }
- }
- }
-
- render() {
- const {
- locale,
- readOnly,
- ...remainingProps
- } = this.props;
-
- const {
- open,
- date,
- provisionalDate,
- value,
- } = this.state;
-
- if (readOnly) {
- const {
- embedded,
- id,
- } = remainingProps;
-
- return (
-
- );
- }
-
- let calendarValue;
-
- if (value) {
- calendarValue = (typeof provisionalDate !== 'undefined') ? provisionalDate : date;
- }
-
- const className = (typeof provisionalDate !== 'undefined') ? styles.provisional : styles.normal;
+ id,
+ } = remainingProps;
return (
-
-
-
-
-
+
);
}
+
+ return (
+
+ );
}
DateInput.propTypes = propTypes;
diff --git a/styles/cspace-input/DateInput.css b/styles/cspace-input/DateInput.css
deleted file mode 100644
index 049841d..0000000
--- a/styles/cspace-input/DateInput.css
+++ /dev/null
@@ -1,25 +0,0 @@
-@value textMedium from '../colors.css';
-@value inputHeight from '../dimensions.css';
-
-.common > input, .common > textarea {
- padding-right: calc(inputHeight - 1px);
- background-image: url(../../images/calDate.svg);
- background-repeat: no-repeat;
- background-position: right;
-}
-
-.common > div {
- min-width: auto;
-}
-
-.normal {
- composes: common;
-}
-
-.provisional {
- composes: common;
-}
-
-.provisional input, .provisional textarea {
- color: textMedium;
-}
diff --git a/styles/react-calendar/calendar.css b/styles/react-calendar/calendar.css
deleted file mode 100644
index a64fe7a..0000000
--- a/styles/react-calendar/calendar.css
+++ /dev/null
@@ -1,111 +0,0 @@
-.react-calendar {
- position: relative;
- width: 300px;
- max-width: 100%;
- background: white;
- font-family: 'Open Sans', Arial, sans-serif;
- font-size: 14px;
- font-weight: 400;
- color: rgb(70, 70, 70);
-}
-.react-calendar,
-.react-calendar *,
-.react-calendar *:before,
-.react-calendar *:after {
- -moz-box-sizing: border-box;
- -webkit-box-sizing: border-box;
- box-sizing: border-box;
-}
-.react-calendar button {
- margin: 0;
- border: 0;
- outline: none;
- font-family: 'Open Sans', Arial, sans-serif;
- font-size: 14px;
- font-weight: 400;
- color: rgb(70, 70, 70);
-}
-.react-calendar button::-moz-focus-inner {
- border: 0;
- padding: 0;
-}
-.react-calendar__navigation {
- border-bottom: 1px solid rgb(220, 220, 220);
-}
-.react-calendar__navigation button {
- min-width: 44px;
- background: none;
- padding: 8px 0;
-}
-.react-calendar__navigation button:not([disabled]):hover {
- background-color: #F0F5FB;
-}
-.react-calendar__navigation button:not([disabled]):focus {
- outline: 1px dotted black;
- z-index: 1;
-}
-.react-calendar__month-view__weekdays {
- text-align: center;
- border-bottom: 1px solid rgb(220, 220, 220);
-}
-.react-calendar__month-view__weekdays__weekday {
- padding: 8px 0;
- font-size: 12px;
- color: rgb(160, 160, 160);
-}
-.react-calendar__month-view__weekdays__weekday > abbr {
- text-decoration: none;
-}
-.react-calendar__month-view__weekNumbers {
- font-weight: bold;
-}
-.react-calendar__month-view__weekNumbers .react-calendar__tile {
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: .75em;
- padding: calc(1em) calc(0.66666667em);
-}
-button.react-calendar__month-view__days__day--neighboringMonth {
- color: rgb(160, 160, 160);
-}
-.react-calendar__year-view .react-calendar__tile,
-.react-calendar__decade-view .react-calendar__tile,
-.react-calendar__century-view .react-calendar__tile {
- padding: 8px 0;
-}
-.react-calendar__tile {
- max-width: 100%;
- text-align: center;
- padding: 8px 0;
- background: none;
-}
-button.react-calendar__tile[disabled] {
- background-color: #f0f0f0;
-}
-button.react-calendar__tile:not([disabled]):hover {
- background-color: #F0F5FB;
-}
-button.react-calendar__tile:not([disabled]):focus {
- outline: 1px dotted black;
- z-index: 1;
-}
-button.react-calendar__tile--hasActive {
- outline: 2px solid rgb(70, 70, 70);
- outline-offset: -1px;
- z-index: 1;
-}
-button.react-calendar__tile--active {
- outline: 2px solid rgb(70, 70, 70);
- outline-offset: -1px;
- z-index: 1;
-}
-button.react-calendar__tile--now > abbr {
- border-radius: 3px;
- padding: 2px 8px;
- background-color: rgb(120, 120, 120);
- color: white;
-}
-.react-calendar--selectRange .react-calendar__tile--hover {
- background-color: #e6e6e6;
-}
diff --git a/test/specs/components/DateInput.spec.jsx b/test/specs/components/DateInput.spec.jsx
index 458e102..0160543 100644
--- a/test/specs/components/DateInput.spec.jsx
+++ b/test/specs/components/DateInput.spec.jsx
@@ -3,7 +3,6 @@
import React from 'react';
import { Simulate } from 'react-dom/test-utils';
import DateInput from '../../../src/components/DateInput';
-import commitHandler from '../../helpers/commitHandler';
import createTestContainer from '../../helpers/createTestContainer';
import { render } from '../../helpers/renderHelpers';
import { isInput } from '../../../src/helpers/inputHelpers';
@@ -24,127 +23,6 @@ describe('DateInput', function suite() {
isInput().should.equal(true);
});
- it('should be closed initially', function test() {
- const EnhancedDateInput = commitHandler(DateInput);
- render(, this.container);
-
- expect(this.container.querySelector('.react-calendar')).to.equal(null);
- });
-
- it('should render with a tooltip when provided', function test() {
- const tooltip = 'test';
- render(, this.container);
-
- const input = this.container.querySelector('input');
- expect(input.title).to.equal(tooltip);
- });
-
- it('should not open on focus', function test() {
- render(, this.container);
-
- const input = this.container.querySelector('input');
-
- Simulate.focus(input);
-
- expect(this.container.querySelector('.react-calendar')).to.equal(null);
- });
-
- it('should open on mouse down', function test() {
- render(, this.container);
-
- const input = this.container.querySelector('input');
-
- Simulate.mouseDown(input);
-
- this.container.querySelector('.react-calendar').should.not.equal(null);
- });
-
- it('should open when the input value changes', function test() {
- render(, this.container);
-
- const input = this.container.querySelector('input');
-
- Simulate.change(input);
-
- this.container.querySelector('.react-calendar').should.not.equal(null);
- });
-
- it('should close the calendar dropdown when tab is pressed in the input', function test() {
- render(
- , this.container,
- );
-
- const input = this.container.querySelector('input');
-
- Simulate.focus(input);
-
- input.value = '1999-04-22';
-
- Simulate.change(input);
-
- this.container.querySelector('.react-calendar').should.not.equal(null);
-
- Simulate.keyDown(input, { key: 'Tab' });
-
- expect(this.container.querySelector('.react-calendar')).to.equal(null);
- });
-
- it('should call onCommit when enter is depressed in the dropdown input', function test() {
- let committedPath = null;
- let committedValue = null;
-
- const handleCommit = (path, value) => {
- committedPath = path;
- committedValue = value;
- };
-
- render(
- , this.container,
- );
-
- const input = this.container.querySelector('input');
-
- input.value = '1999-04-22';
-
- Simulate.change(input);
- Simulate.keyDown(input, { key: 'Enter' });
-
- committedPath.should.deep.equal(['birthDate']);
- committedValue.should.equal('1999-04-22');
- });
-
- it('should not call onCommit when the dropdown input value is unchanged from the initial value', function test() {
- let handlerCalled = false;
-
- const handleCommit = () => {
- handlerCalled = true;
- };
-
- render(
- , this.container,
- );
-
- const input = this.container.querySelector('input');
-
- input.value = '2011-11-02';
-
- Simulate.change(input);
- Simulate.keyPress(input, { key: 'Enter' });
-
- handlerCalled.should.equal(false);
- });
-
it('should call onCommit when a value is selected in the calendar', function test() {
let committedPath = null;
let committedValue = null;
@@ -164,17 +42,13 @@ describe('DateInput', function suite() {
const input = this.container.querySelector('input');
- Simulate.keyDown(input, { key: 'ArrowDown' });
-
- const dateButton = this.container.querySelector('button.react-calendar__month-view__days__day');
-
- Simulate.click(dateButton);
+ Simulate.change(input, { target: { value: '2022-10-02' } });
committedPath.should.deep.equal(['birthDate']);
committedValue.should.not.equal(null);
});
- it('should not call onCommit when the selected calendar date is unchanged from the initial value', function test() {
+ it('should not call onCommit when the input value is unchanged from the initial value', function test() {
let handlerCalled = false;
const handleCommit = () => {
@@ -191,16 +65,12 @@ describe('DateInput', function suite() {
const input = this.container.querySelector('input');
- Simulate.keyDown(input, { key: 'ArrowDown' });
-
- const calendar = this.container.querySelector('.react-calendar');
-
- Simulate.keyDown(calendar, { key: 'Escape' });
+ Simulate.change(input, { target: { value: '2011-11-02' } });
handlerCalled.should.equal(false);
});
- it('should commit a blank value when the calendar closes, even if enter was not pressed', function test() {
+ it('should commit a blank value', function test() {
let committedPath = null;
let committedValue = null;
@@ -218,11 +88,7 @@ describe('DateInput', function suite() {
);
const input = this.container.querySelector('input');
-
- input.value = '';
-
- Simulate.change(input);
- Simulate.blur(input);
+ Simulate.change(input, { target: { value: '' } });
return new Promise((resolve) => {
window.setTimeout(() => {
@@ -253,8 +119,6 @@ describe('DateInput', function suite() {
const input = this.container.querySelector('input');
- input.value = '';
-
Simulate.change(input);
Simulate.keyDown(input, { key: 'Escape' });
@@ -268,17 +132,6 @@ describe('DateInput', function suite() {
});
});
- it('should label weekdays based on locale', function test() {
- render(, this.container);
-
- const input = this.container.querySelector('input');
-
- Simulate.keyDown(input, { key: 'ArrowDown' });
-
- this.container.querySelector('.react-calendar__month-view__weekdays__weekday').textContent
- .should.equal('lun');
- });
-
it('should normalize the value', function test() {
render(
, this.container);
-
- const input = this.container.querySelector('input');
-
- input.value.should.equal('01/01/1995');
- });
-
- it('should display year/month only dates', function test() {
- render(, this.container);
-
- const input = this.container.querySelector('input');
-
- input.value.should.equal('04-1995');
- });
-
- it('should display invalid dates and persist if the field is clicked', function test() {
- render(, this.container);
+ it('should render a LineInput if asText is true', function test() {
+ render(, this.container);
- const input = this.container.querySelector('input');
-
- input.value.should.equal('01/01/1995');
- Simulate.mouseDown(input);
- this.container.querySelector('.react-calendar').should.not.equal(null);
+ const div = this.container.firstElementChild;
- Simulate.keyDown(input, { key: 'Escape' });
- input.value.should.equal('01/01/1995');
+ div.className.should.contain('cspace-input-LineInput--normal');
+ div.textContent.should.equal('2017-03-11');
});
});