Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🪟 🎉 Add datepicker for date/date-time fields in connector form #19678

Merged
merged 42 commits into from
Dec 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
26cc516
First implementation of DatePicker
timroes Nov 1, 2022
4d8da56
Merge branch 'master' into tim/datepicker
timroes Nov 2, 2022
b2a5971
styling pass
josephkmh Nov 21, 2022
6776ca1
Merge branch 'master' into joey/datepicker
josephkmh Nov 21, 2022
3937e52
regenerate lockfile after merge conflict
josephkmh Nov 21, 2022
f47c77f
add tests to dayjs timezone trickery
josephkmh Nov 21, 2022
ce11b18
disable stylelint on library css classnames
josephkmh Nov 22, 2022
a8afdba
fix out of month day styling
josephkmh Nov 23, 2022
4103239
add another test strategy
josephkmh Nov 23, 2022
12fc269
set touched on change
josephkmh Nov 23, 2022
da405fe
remove debug output
josephkmh Nov 23, 2022
2c3e617
remove debugging condition
josephkmh Nov 23, 2022
b537755
move datepicker to button trigger
josephkmh Nov 23, 2022
743a157
position datepicker button inside input
josephkmh Nov 24, 2022
50263b9
add feature flag for datepicker
josephkmh Nov 24, 2022
ffde1a8
remove fb marketin debugging condition
josephkmh Nov 24, 2022
aaf871a
adjust styling
josephkmh Nov 24, 2022
00c4803
avoid undefined initial value
josephkmh Nov 24, 2022
5db59f9
add pattern for registering locales
josephkmh Nov 24, 2022
e392762
clean up props
josephkmh Nov 25, 2022
20802bf
swap icon
josephkmh Nov 25, 2022
5ff2c1d
add some test cases for whole component
josephkmh Nov 25, 2022
db44e6f
udpate comment
josephkmh Nov 25, 2022
2a8b78b
use experiment instead of feature
josephkmh Nov 25, 2022
e785315
add automatic open on input focus
josephkmh Nov 25, 2022
72406b3
revert hiding example value
josephkmh Nov 25, 2022
6b9b49d
remove comment
josephkmh Nov 25, 2022
95264da
account for dayjs initializing invalid date
josephkmh Nov 28, 2022
0401893
simplify equivalent date function
josephkmh Nov 28, 2022
6c97808
add test descriptions
josephkmh Nov 28, 2022
f2e7bca
fix text overflow
josephkmh Nov 28, 2022
16884e7
add datepicker story
josephkmh Nov 28, 2022
400ff19
Merge branch 'master' into joey/datepicker
josephkmh Nov 28, 2022
9022e82
use setupTests
josephkmh Nov 29, 2022
7d69401
fix button width
josephkmh Nov 30, 2022
d4109aa
forward ref in Input
josephkmh Nov 30, 2022
01380f1
focus input on datepicker change
josephkmh Nov 30, 2022
7a57ca7
Update airbyte-webapp/src/components/ui/DatePicker/DatePicker.module.…
josephkmh Nov 30, 2022
d00f94b
fix blur event type
josephkmh Dec 3, 2022
86e95e1
remove unnecessary event arg
josephkmh Dec 3, 2022
e0226b1
add utc to time caption
josephkmh Dec 5, 2022
1751d79
Merge branch 'master' into joey/datepicker
josephkmh Dec 7, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions airbyte-webapp/.storybook/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { withProviders } from "./withProvider";

import "!style-loader!css-loader!sass-loader!../public/index.css";
import "../src/scss/global.scss";
import "../src/globals";

addDecorator(withProviders);

Expand Down
19,860 changes: 4,854 additions & 15,006 deletions airbyte-webapp/package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions airbyte-webapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"mdast": "^3.0.0",
"query-string": "^6.13.1",
"react": "^17.0.2",
"react-datepicker": "^4.8.0",
"react-dom": "^17.0.2",
"react-helmet-async": "^1.3.0",
"react-intl": "^6.1.1",
Expand Down Expand Up @@ -101,6 +102,7 @@
"@types/node": "^17.0.40",
"@types/query-string": "^6.3.0",
"@types/react": "^17.0.39",
"@types/react-datepicker": "^4.8.0",
"@types/react-dom": "^17.0.11",
"@types/react-helmet": "^6.1.5",
"@types/react-lazylog": "^4.5.1",
Expand Down Expand Up @@ -138,6 +140,7 @@
"stylelint-config-standard": "^26.0.0",
"stylelint-config-standard-scss": "^5.0.0",
"tar": "^6.1.11",
"timezone-mock": "^1.3.4",
"tmpl": "^1.0.5",
"ts-node": "^10.8.1",
"typescript": "^4.7.3"
Expand Down
146 changes: 146 additions & 0 deletions airbyte-webapp/src/components/ui/DatePicker/DatePicker.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/* stylelint-disable selector-class-pattern, no-descending-specificity */

@use "scss/colors";
@use "scss/fonts";
@use "scss/variables";
@use "scss/z-indices";

.wrapper {
position: relative;
}

.datepickerButtonContainer {
position: absolute;
right: 0;
top: 0;
display: flex;
height: 100%;
align-items: center;
justify-content: center;
border: none;
}

.datepickerButton svg {
font-size: 14px;
}

.popup {
z-index: z-indices.$datepicker;
}

.input {
padding-right: 25px;
}

:global(.react-datepicker) {
display: flex;
color: colors.$dark-blue-900;
font-family: fonts.$primary;
border: variables.$border-thick solid colors.$grey-100;
border-radius: variables.$border-radius-md;
box-shadow: 2px 2px 20px -6px colors.$grey-200;
}

/** Main calendar area **/
:global(.react-datepicker__header) {
josephkmh marked this conversation as resolved.
Show resolved Hide resolved
background-color: colors.$grey-50;
border-top-right-radius: variables.$border-radius-md;
border-top-left-radius: variables.$border-radius-md;
border-bottom: none;
}

:global(.react-datepicker__header.react-datepicker__header--has-time-select) {
border-top-right-radius: 0;
}

:global(.react-datepicker__header:not(.react-datepicker__header--has-time-select)) {
border-top-right-radius: variables.$border-radius-md;
}

:global(.react-datepicker__day-name) {
color: colors.$grey-300;
}

:global(.react-datepicker__current-month) {
color: colors.$dark-blue-900;
font-weight: 500;
margin-bottom: variables.$spacing-md;
}

:global(.react-datepicker__day) {
color: colors.$dark-blue-900;
background-color: transparent;
border-radius: variables.$border-radius-xs;

&:hover {
background-color: colors.$grey-100;
}
}

:global(.react-datepicker__day--outside-month) {
color: colors.$grey-300;
}

:global(.react-datepicker__day--today) {
background-color: colors.$grey-50;
font-weight: 700;
}

:global(.react-datepicker__day--selected) {
color: colors.$white;
background-color: colors.$blue-400;
font-weight: 700;

&:hover {
background-color: colors.$blue-500;
}
}

:global(.react-datepicker__navigation-icon::before) {
border-width: 2px 2px 0 0;
}

/** Time **/
:global(.react-datepicker__time-container) {
border-left-color: colors.$grey-100;
border-left-width: variables.$border-thick;
border-bottom-right-radius: variables.$border-radius-md;
overflow: hidden;
}

:global(.react-datepicker-time__header) {
font-weight: 500;
color: colors.$dark-blue-900;
}

:global(.react-datepicker__time-list-item) {
background-color: transparent;
display: flex;
justify-content: center;
align-items: center;

&:hover {
background-color: colors.$grey-100;
}
}

:global(.react-datepicker__time-container
.react-datepicker__time
.react-datepicker__time-box
ul.react-datepicker__time-list
li.react-datepicker__time-list-item) {
justify-content: center;
}

:global(.react-datepicker__time-container
.react-datepicker__time
.react-datepicker__time-box
ul.react-datepicker__time-list
li.react-datepicker__time-list-item--selected) {
color: colors.$white;
background-color: colors.$blue-400;

&:hover {
background-color: colors.$blue-500;
}
}
144 changes: 144 additions & 0 deletions airbyte-webapp/src/components/ui/DatePicker/DatePicker.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import dayjs from "dayjs";
import { TestWrapper } from "test-utils/testutils";
import timezoneMock from "timezone-mock";

import { DatePicker, toEquivalentLocalTime } from "./DatePicker";

describe(`${toEquivalentLocalTime.name}`, () => {
// Seems silly, but dayjs has a bug when formatting years, so this is a useful test:
// https://github.com/iamkun/dayjs/issues/1745
it("handles a date in the year 1", () => {
const TEST_UTC_TIMESTAMP = "0001-12-01T09:00:00Z";

const result = toEquivalentLocalTime(TEST_UTC_TIMESTAMP);

expect(result).toEqual(undefined);
});

it("handles an invalid date", () => {
const TEST_UTC_TIMESTAMP = "not a date";

const result = toEquivalentLocalTime(TEST_UTC_TIMESTAMP);

expect(result).toEqual(undefined);
});

it("outputs the same YYYY-MM-DDTHH:mm:ss", () => {
const TEST_UTC_TIMESTAMP = "2000-01-01T12:00:00Z";

const result = toEquivalentLocalTime(TEST_UTC_TIMESTAMP);

// Regardless of the timezone, the local time should be the same
expect(result?.toISOString().substring(0, 19)).toEqual(TEST_UTC_TIMESTAMP.substring(0, 19));
});

it("converts utc time to equivalent local time in PST", () => {
timezoneMock.register("US/Pacific");
const TEST_TIMEZONE_UTC_OFFSET_IN_MINUTES = 480; // corresponds to the registered mock timezone
const TEST_UTC_TIMESTAMP = "2022-01-01T00:00:00Z";

const expectedDateObject = dayjs
.utc(TEST_UTC_TIMESTAMP)
.add(TEST_TIMEZONE_UTC_OFFSET_IN_MINUTES, "minutes")
.toDate();

expect(toEquivalentLocalTime(TEST_UTC_TIMESTAMP)).toEqual(expectedDateObject);
});

it("converts utc time to equivalent local time in EST", () => {
timezoneMock.register("US/Eastern");
const TEST_TIMEZONE_UTC_OFFSET_IN_MINUTES = 300; // corresponds to the registered mock timezone
const TEST_UTC_TIMESTAMP = "2022-01-01T00:00:00Z";

const expectedDateObject = dayjs
.utc(TEST_UTC_TIMESTAMP)
.add(TEST_TIMEZONE_UTC_OFFSET_IN_MINUTES, "minutes")
.toDate();

expect(toEquivalentLocalTime(TEST_UTC_TIMESTAMP)).toEqual(expectedDateObject);
});

it("keeps a utc timestamp exactly the same", () => {
timezoneMock.register("UTC");
const TEST_UTC_TIMESTAMP = "2022-01-01T00:00:00Z";

const expectedDateObject = dayjs.utc(TEST_UTC_TIMESTAMP).toDate();

expect(toEquivalentLocalTime(TEST_UTC_TIMESTAMP)).toEqual(expectedDateObject);
});

afterEach(() => {
// Return global Date() object to system behavior
timezoneMock.unregister();
});
});

describe(`${DatePicker.name}`, () => {
it("allows typing a date manually", async () => {
const MOCK_DESIRED_DATETIME = "2010-09-12T00:00:00Z";
let mockValue = "";
render(
<TestWrapper>
<DatePicker
onChange={(value) => {
// necessary for controlled inputs https://github.com/testing-library/user-event/issues/387#issuecomment-819868799
mockValue = mockValue + value;
}}
value={mockValue}
/>
</TestWrapper>
);

const input = screen.getByTestId("input");
await userEvent.type(input, MOCK_DESIRED_DATETIME, { delay: 1 });

expect(mockValue).toEqual(MOCK_DESIRED_DATETIME);
});

it("allows selecting a date from the datepicker", async () => {
jest.useFakeTimers().setSystemTime(new Date("2010-09-05"));
const MOCK_DESIRED_DATETIME = "2010-09-12";
let mockValue = "";
render(
<TestWrapper>
<DatePicker
onChange={(value) => {
// necessary for controlled inputs https://github.com/testing-library/user-event/issues/387#issuecomment-819868799
mockValue = mockValue + value;
}}
value={mockValue}
/>
</TestWrapper>
);

const datepicker = screen.getByLabelText("Open datepicker");
userEvent.click(datepicker);
const date = screen.getByLabelText("Choose Sunday, September 12th, 2010");
userEvent.click(date);

expect(mockValue).toEqual(MOCK_DESIRED_DATETIME);
jest.useRealTimers();
});

it("focuses the input after selecting a date from the datepicker", async () => {
jest.useFakeTimers().setSystemTime(new Date("2010-09-05"));
let mockValue = "";
render(
<TestWrapper>
<DatePicker onChange={(value) => (mockValue = value)} value={mockValue} />
</TestWrapper>
);

const datepicker = screen.getByLabelText("Open datepicker");
userEvent.click(datepicker);
const date = screen.getByLabelText("Choose Sunday, September 12th, 2010");
userEvent.click(date);

const input = screen.getByTestId("input");

expect(input).toHaveFocus();
jest.useRealTimers();
});
});
Loading