Skip to content

Commit

Permalink
refactor: clean up api
Browse files Browse the repository at this point in the history
  • Loading branch information
adriansberg committed Oct 5, 2023
1 parent f827bbe commit 424bbde
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,7 @@ afterEach(function () {

describe('departure date selector', function () {
it('should default to "Now"', async () => {
const output = render(
<DepartureDateSelector
onStateChange={() => {}}
onDateChange={() => {}}
onTimeChange={() => {}}
/>,
);
const output = render(<DepartureDateSelector onChange={() => {}} />);

expect(
output.getByRole('radio', {
Expand All @@ -28,13 +22,7 @@ describe('departure date selector', function () {
});

it('should not show date and time selector when "Now" is selected', async () => {
const output = render(
<DepartureDateSelector
onStateChange={() => {}}
onDateChange={() => {}}
onTimeChange={() => {}}
/>,
);
const output = render(<DepartureDateSelector onChange={() => {}} />);

expect(output.queryByText('Dato')).not.toBeInTheDocument();
expect(output.queryByText('Tid')).not.toBeInTheDocument();
Expand All @@ -43,10 +31,8 @@ describe('departure date selector', function () {
it('should use initialState for default selected', async () => {
const output = render(
<DepartureDateSelector
initialState={DepartureDateState.Arrival}
onStateChange={() => {}}
onDateChange={() => {}}
onTimeChange={() => {}}
initialState={{ type: DepartureDateState.Arrival, dateTime: 0 }}
onChange={() => {}}
/>,
);

Expand All @@ -58,10 +44,8 @@ describe('departure date selector', function () {
it('should show date and time selector when "Arrival" is selected', async () => {
const output = render(
<DepartureDateSelector
initialState={DepartureDateState.Arrival}
onStateChange={() => {}}
onDateChange={() => {}}
onTimeChange={() => {}}
initialState={{ type: DepartureDateState.Arrival, dateTime: 0 }}
onChange={() => {}}
/>,
);

Expand All @@ -72,10 +56,8 @@ describe('departure date selector', function () {
it('should show date and time selector when "Departure" is selected', async () => {
const output = render(
<DepartureDateSelector
initialState={DepartureDateState.Departure}
onStateChange={() => {}}
onDateChange={() => {}}
onTimeChange={() => {}}
initialState={{ type: DepartureDateState.Departure, dateTime: 0 }}
onChange={() => {}}
/>,
);

Expand All @@ -84,13 +66,7 @@ describe('departure date selector', function () {
});

it('should change selection with keyboard', async () => {
const output = render(
<DepartureDateSelector
onStateChange={() => {}}
onDateChange={() => {}}
onTimeChange={() => {}}
/>,
);
const output = render(<DepartureDateSelector onChange={() => {}} />);

const radio = output.getByRole('radio', { name: 'Nå' });
radio.focus();
Expand All @@ -107,13 +83,7 @@ describe('departure date selector', function () {
});

it('should change selection when clicked', async () => {
const output = render(
<DepartureDateSelector
onStateChange={() => {}}
onDateChange={() => {}}
onTimeChange={() => {}}
/>,
);
const output = render(<DepartureDateSelector onChange={() => {}} />);

const radio = output.getByRole('radio', {
name: 'Ankomst',
Expand All @@ -128,52 +98,42 @@ describe('departure date selector', function () {

it('should call onStateChange', async () => {
const onStateChange = vi.fn();
const output = render(
<DepartureDateSelector
onStateChange={onStateChange}
onDateChange={() => {}}
onTimeChange={() => {}}
/>,
);
const output = render(<DepartureDateSelector onChange={onStateChange} />);

output.getByRole('radio', { name: 'Ankomst' }).click();

expect(onStateChange).toHaveBeenCalledWith(DepartureDateState.Arrival);
expect(onStateChange).toHaveBeenCalled();
});

it('should call onDateChange', async () => {
const onDateChange = vi.fn();
const onChange = vi.fn();
const output = render(
<DepartureDateSelector
initialState={DepartureDateState.Arrival}
onStateChange={() => {}}
onDateChange={onDateChange}
onTimeChange={() => {}}
initialState={{ type: DepartureDateState.Arrival, dateTime: 0 }}
onChange={onChange}
/>,
);

const date = output.getByLabelText('Dato');

fireEvent.change(date, { target: { value: '2100-01-01' } });

expect(onDateChange).toHaveBeenCalledWith(new Date('2100-01-01'));
expect(onChange).toHaveBeenCalled();
});

it('should call onTimeChange', async () => {
const onTimeChange = vi.fn();
const onChange = vi.fn();
const output = render(
<DepartureDateSelector
initialState={DepartureDateState.Arrival}
onStateChange={() => {}}
onDateChange={() => {}}
onTimeChange={onTimeChange}
initialState={{ type: DepartureDateState.Arrival, dateTime: 0 }}
onChange={onChange}
/>,
);

const time = output.getByLabelText('Tid');

fireEvent.change(time, { target: { value: '00:00' } });

expect(onTimeChange).toHaveBeenCalledWith('00:00');
expect(onChange).toHaveBeenCalled();
});
});
67 changes: 52 additions & 15 deletions src/components/departure-date-selector/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,31 @@ export enum DepartureDateState {
Departure = 'departure',
}

export type DepartureDate =
| {
type: DepartureDateState.Now;
}
| {
type: DepartureDateState.Arrival;
dateTime: number;
}
| {
type: DepartureDateState.Departure;
dateTime: number;
};

type DepartureDateSelectorProps = {
initialState?: DepartureDateState;
onStateChange: (state: DepartureDateState) => void;
onDateChange: (date: Date) => void;
onTimeChange: (time: string) => void;
initialState?: DepartureDate;
onChange: (state: DepartureDate) => void;
};

export default function DepartureDateSelector({
initialState = DepartureDateState.Now,
onStateChange,
onDateChange,
onTimeChange,
initialState = { type: DepartureDateState.Now },
onChange,
}: DepartureDateSelectorProps) {
const { t } = useTranslation();
const [selectedOption, setSelectedOption] =
useState<DepartureDateState>(initialState);
useState<DepartureDate>(initialState);
const [selectedDate, setSelectedDate] = useState(new Date());
const [selectedTime, setSelectedTime] = useState(() => {
const now = new Date();
Expand All @@ -35,17 +44,45 @@ export default function DepartureDateSelector({
});

const internalOnStateChange = (state: DepartureDateState) => {
setSelectedOption(state);
onStateChange(state);
const newState =
state === DepartureDateState.Now
? {
type: state,
}
: {
type: state,
dateTime: new Date(
selectedDate.toISOString().slice(0, 10) + 'T' + selectedTime,
).getTime(),
};

setSelectedOption(newState);

onChange(newState);
};

const internalOnDateChange = (event: ChangeEvent<HTMLInputElement>) => {
if (!event.target.value) return;

setSelectedDate(new Date(event.target.value));
onDateChange(new Date(event.target.value));

onChange({
type: selectedOption.type,
dateTime: new Date(event.target.value + 'T' + selectedTime).getTime(),
});
};

const internalOnTimeChange = (event: ChangeEvent<HTMLInputElement>) => {
if (!event.target.value) return;

setSelectedTime(event.target.value);
onTimeChange(event.target.value);

onChange({
type: selectedOption.type,
dateTime: new Date(
selectedDate.toISOString().slice(0, 10) + 'T' + event.target.value,
).getTime(),
});
};

return (
Expand All @@ -58,7 +95,7 @@ export default function DepartureDateSelector({
type="radio"
name="departureDateSelector"
value={state}
checked={selectedOption === state}
checked={selectedOption.type === state}
onChange={() => internalOnStateChange(state)}
aria-label={stateToLabel(state, t)}
/>
Expand All @@ -68,7 +105,7 @@ export default function DepartureDateSelector({
))}
</div>

{selectedOption !== DepartureDateState.Now && (
{selectedOption.type !== DepartureDateState.Now && (
<div className={style.dateAndTimeSelectors}>
<div className={style.dateSelector}>
<label htmlFor="departureDateSelector">
Expand Down
17 changes: 7 additions & 10 deletions src/page-modules/departures/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import type { WithGlobalData } from "@atb/layouts/global-data";
import type { GeocoderFeature } from "@atb/page-modules/departures";
import { PageText, useTranslation } from "@atb/translations";
import { PropsWithChildren, useState } from "react";
import { FormEventHandler, PropsWithChildren, useState } from "react";
import Search from "@atb/components/search";
import { Button } from "@atb/components/button";
import style from "./departures.module.css";
import { useRouter } from "next/router";
import DepartureDateSelector, {
DepartureDate,
DepartureDateState,
} from '@atb/components/departure-date-selector';

Expand All @@ -17,11 +18,9 @@ function DeparturesLayout({ children }: DeparturesLayoutProps) {
const router = useRouter();

const [selectedFeature, setSelectedFeature] = useState<GeocoderFeature>();
const [departureDateState, setDepartureDateState] = useState(
DepartureDateState.Now,
);
const [_departureDate, setDepartureDate] = useState<Date>();
const [_departureTime, setDepartureTime] = useState<string>();
const [departureDate, setDepartureDate] = useState<DepartureDate>({
type: DepartureDateState.Now,
});

const onSubmitHandler: FormEventHandler<HTMLFormElement> = (e) => {
e.preventDefault();
Expand Down Expand Up @@ -60,10 +59,8 @@ function DeparturesLayout({ children }: DeparturesLayoutProps) {
</p>

<DepartureDateSelector
initialState={departureDateState}
onStateChange={setDepartureDateState}
onDateChange={setDepartureDate}
onTimeChange={setDepartureTime}
initialState={departureDate}
onChange={setDepartureDate}
/>
</div>

Expand Down

0 comments on commit 424bbde

Please sign in to comment.