Skip to content

Commit

Permalink
Feature: APP-2731 - Implement InputTime (#67)
Browse files Browse the repository at this point in the history
  • Loading branch information
Fabricevladimir authored Jan 25, 2024
1 parent a766bd0 commit 43fd497
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## [Unreleased]

### Added

- Implement `InputTime` component

## [1.0.9] - 2024-01-23

### Fixed
Expand Down
1 change: 1 addition & 0 deletions src/components/input/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './inputDate';
export * from './inputNumberMax';
export * from './inputSearch';
export * from './inputText';
export * from './inputTime';
1 change: 1 addition & 0 deletions src/components/input/inputTime/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { InputTime, type IInputTimeProps } from './inputTime';
49 changes: 49 additions & 0 deletions src/components/input/inputTime/inputTime.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { Meta, StoryObj } from '@storybook/react';
import { useState, type ChangeEvent } from 'react';
import { InputTime, type IInputTimeProps } from './inputTime';

const meta: Meta<typeof InputTime> = {
title: 'components/Input/InputTime',
component: InputTime,
tags: ['autodocs'],
parameters: {
design: {
type: 'figma',
url: 'https://www.figma.com/file/ISSDryshtEpB7SUSdNqAcw/branch/jfKRr1V9evJUp1uBeyP3Zz/Aragon-ODS?type=design&node-id=10080-1869&mode=design&t=DMhjcmSjhuHsGH3N-0',
},
},
};

type Story = StoryObj<typeof InputTime>;

/**
* Default usage example of the `InputTime` component.
*
* Note: The time picker is disabled by default on Firefox and can be enabled by setting the
* `dom.forms.datetime.timepicker` preference to `true`. Please be aware that this may enable experimental features.
*
* More information about how to change Firefox browser preferences can be found in the
* [Firefox documentation](https://support.mozilla.org/en-US/kb/about-config-editor-firefox).
*/

export const Default: Story = {
args: {},
};

const ControlledComponent = (props: IInputTimeProps) => {
const [value, setValue] = useState<string>('13:00');

const handleChange = (event: ChangeEvent<HTMLInputElement>) => setValue(event.target.value);

return <InputTime value={value} onChange={handleChange} {...props} />;
};

/**
* Usage example of a controlled `InputTime` component.
*/
export const Controlled: Story = {
args: { step: 130 },
render: (props) => <ControlledComponent {...props} />,
};

export default meta;
63 changes: 63 additions & 0 deletions src/components/input/inputTime/inputTime.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { fireEvent, render, screen, within } from '@testing-library/react';
import React from 'react';
import * as MergeRefs from 'react-merge-refs';
import { IconType } from '../../icon';
import { InputTime, type IInputTimeProps } from './inputTime';

describe('<InputTime /> component', () => {
const useRefMock = jest.spyOn(React, 'useRef');
const mergeRefMock = jest.spyOn(MergeRefs, 'mergeRefs');

afterEach(() => {
useRefMock.mockReset();
mergeRefMock.mockReset();
});

const createTestComponent = (props?: Partial<IInputTimeProps>) => {
const completeProps = { ...props };

return <InputTime {...completeProps} />;
};

it('renders a time input', () => {
const label = 'Test label';
render(createTestComponent({ label }));

const timeInput = screen.getByLabelText<HTMLInputElement>(label);

expect(timeInput).toBeInTheDocument();
expect(timeInput.type).toEqual('time');
});

it('renders a button which opens the time picker on click', () => {
const showPicker = jest.fn();
useRefMock.mockReturnValue({ current: { showPicker } });
mergeRefMock.mockReturnValue(() => null);

render(createTestComponent());
const timeButton = screen.getByRole('button');

expect(timeButton).toBeInTheDocument();
expect(within(timeButton).getByTestId(IconType.CLOCK)).toBeInTheDocument();

fireEvent.click(timeButton);
expect(showPicker).toHaveBeenCalled();
});

it('renders the input as disabled when the isDisabled property is set to true', () => {
const isDisabled = true;
const label = 'Test label';

render(createTestComponent({ label, isDisabled }));

expect(screen.getByLabelText(label)).toBeDisabled();
});

it('hides the time picker button when the disabled property is set to true', () => {
const isDisabled = true;

render(createTestComponent({ isDisabled }));

expect(screen.queryByRole('button')).not.toBeInTheDocument();
});
});
45 changes: 45 additions & 0 deletions src/components/input/inputTime/inputTime.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import classNames from 'classnames';
import { forwardRef, useRef } from 'react';
import { mergeRefs } from 'react-merge-refs';
import { Button } from '../../button';
import { IconType } from '../../icon';
import { useInputProps } from '../hooks';
import { InputContainer, type IInputComponentProps } from '../inputContainer';

export interface IInputTimeProps extends Omit<IInputComponentProps, 'maxLength' | 'inputLength'> {}

export const InputTime = forwardRef<HTMLInputElement, IInputTimeProps>((props, ref) => {
const { containerProps, inputProps } = useInputProps(props);

const { className: containerClassName, ...otherContainerProps } = containerProps;
const { className: inputClassName, disabled, ...otherInputProps } = inputProps;

const inputRef = useRef<HTMLInputElement>(null);

const handleClockClick = () => {
inputRef.current?.showPicker();
};

return (
<InputContainer className={containerClassName} {...otherContainerProps}>
<input
type="time"
ref={mergeRefs([inputRef, ref])}
disabled={disabled}
className={classNames('calendar-icon:hidden calendar-icon:appearance-none', inputClassName)}
{...otherInputProps}
/>
{!disabled && (
<Button
variant="tertiary"
size="sm"
iconLeft={IconType.CLOCK}
className="mr-2 shrink-0"
onClick={handleClockClick}
/>
)}
</InputContainer>
);
});

InputTime.displayName = 'InputTime';

0 comments on commit 43fd497

Please sign in to comment.