Skip to content

Commit

Permalink
Add DateTimePicker (#1220)
Browse files Browse the repository at this point in the history
Ports the DateTimePicker over from rails-server

Converted to TypeScript and simplified the props a little bit in the process
  • Loading branch information
gabescarbrough authored May 16, 2024
1 parent 1497898 commit 2ab3bb5
Show file tree
Hide file tree
Showing 9 changed files with 616 additions and 0 deletions.
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@
"@tiptap/pm": "^2.3.1",
"@tiptap/react": "^2.3.1",
"@tiptap/suggestion": "^2.3.1",
"date-fns": "^3.6.0",
"date-fns-tz": "^3.1.3",
"react-bootstrap": "^2.10.2",
"react-currency-input-field": "^3.8.0",
"react-datepicker": "^6.9.0",
"react-loading-skeleton": "^3.4.0",
"react-router-dom": "^5.3.4",
"react-select": "^5.8.0",
Expand Down Expand Up @@ -67,6 +70,8 @@
"@popperjs/core": "^2.5.3",
"bootstrap": "5.1",
"classnames": "^2.2.5",
"date-fns": "^3.6.0",
"date-fns-tz": "^3.1.3",
"prop-types": "^15.6.1",
"react": "^18.2.0",
"react-copy-to-clipboard": "^5.0.2",
Expand Down
143 changes: 143 additions & 0 deletions src/DateTimePicker/DateTimePicker.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
@import 'react-datepicker/dist/react-datepicker.css';
@import '../../scss/theme.scss';

.date-time-picker {
align-items: flex-start;
display: flex;
flex-direction: column;
justify-content: flex-start;
width: 100%;

@include media-breakpoint-up(sm) {
align-items: center;
flex-direction: row;
}

.react-datepicker__header {
background-color: var(--ux-white);
border-bottom: 1px solid var(--ux-gray-200);
}

.react-datepicker__day-names {
font-weight: var(--synth-font-weight-bold);
}

.react-datepicker__day--outside-month {
color: var(--ux-gray-300);
}


.date-time-picker {
&__input-group {
background-color: var(--ux-white);
border-radius: var(--ux-border-radius);
border: thin solid var(--ux-gray-400);
padding: .46875rem .75rem;
justify-content: space-between;
width: inherit;
}
}

.react-datepicker {
@include font-type-30;
border: none;
box-shadow: 0 1px 3px var(--ux-gray-400);
}

.react-datepicker__time {
@include font-type-20;
}

.react-datepicker__day--selected {
background: var(--ux-blue-500);
}

.react-datepicker__day--keyboard-selected {
background: none;
color: var(--ux-black);
}

.react-datepicker__time-container
.react-datepicker__time
.react-datepicker__time-box
ul.react-datepicker__time-list
li.react-datepicker__time-list-item--selected {
background: var(--ux-blue-500);
}

.react-datepicker__day--today {
color: var(--ux-blue-500);
}

.react-datepicker__day--selected {
color: var(--ux-white);
}

.react-datepicker__triangle {
display: none;
}

// Override form-control's default greying of read only inputs
input:read-only, .form-control[readonly] {
background-color: var(--ux-white);
}

.form-control:focus {
box-shadow: none;
border: 1px solid $input-focus-border-color;
}

.react-datepicker {
width: 100%;

> div:first-child {
width: 100%;
}

&:not(:first-child) {
margin-top: .5rem;

@include media-breakpoint-up(sm) {
margin-top: 0;
margin-left: .5rem;
}
}
}
}


// override some of the form group invalid styles
.FormGroup--is-invalid
.date-time-picker
select {
border: thin solid var(--ux-gray-400);
border-radius: var(--ux-border-radius);
}

// override some of the form group invalid styles
// inputs need more specificity to override the above styling
.FormGroup--is-invalid
.date-time-picker
.react-datepicker-wrapper
.react-datepicker__input-container
input {
border: thin solid var(--ux-red);
}

// Undoing some styles when this is nested within a bootstrap table
.table .date-time-picker {
td, th {
border-top: 0;
padding: 0;
vertical-align: middle;
}

thead th {
border-bottom: 0;
vertical-align: middle;
}
}

.react-datepicker-wrapper {
width: 100%;
}
24 changes: 24 additions & 0 deletions src/DateTimePicker/DateTimePicker.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';

import { DateTimePicker } from '.';

export default {
title: 'Components/DateTimePicker',
component: DateTimePicker,
};

export const Default = () => (
<DateTimePicker />
);

export const EnforcedInput = () => (
<DateTimePicker showPickerEnforcedInput />
);

export const ShowMonthAndYearSelects = () => (
<DateTimePicker showMonthAndYearSelects />
);

export const ShowTimeSelect = () => (
<DateTimePicker showTimeSelect />
);
60 changes: 60 additions & 0 deletions src/DateTimePicker/DateTimePicker.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import DateTimePicker, { DateTimePickerProps } from './DateTimePicker';

const PLACEHOLDER = 'YYYY-MM-DD';

const VALID_DATE = '1999-12-31';
const INVALID_DATE = '99999';

describe('DateTimePicker', () => {
function Setup(overrides: DateTimePickerProps) {
return (
<DateTimePicker
{...overrides}
/>
);
}

describe('when initializing', () => {
describe('when passed a (date) prop', () => {
it('sets input value', () => {
render(<Setup date={VALID_DATE} />);

expect(screen.getByDisplayValue(VALID_DATE)).toBeInTheDocument();
});
});
});

describe('interactions', () => {
describe('when typing in date', () => {
describe('with a valid value', () => {
it('keeps value', async () => {
render(<Setup />);

const input = screen.getByPlaceholderText(PLACEHOLDER);
userEvent.type(input, `${VALID_DATE}{enter}`);

await waitFor(() => {
expect(input).toHaveValue(VALID_DATE);
});
});
});

describe('with an invalid value', () => {
it('clears value', async () => {
render(<Setup />);

const input = screen.getByPlaceholderText(PLACEHOLDER);
userEvent.type(input, `${INVALID_DATE}{enter}`);

await waitFor(() => {
expect(input).toHaveValue('');
});
});
});
});
});
});
Loading

0 comments on commit 2ab3bb5

Please sign in to comment.