From 5f38ab15d2a8bff72bcc7c8cbbe71f658157fbcf Mon Sep 17 00:00:00 2001 From: Ruggero Date: Mon, 22 Jan 2024 13:20:17 +0100 Subject: [PATCH] feat: APP-2730 - Implement InputDate component (#55) --- CHANGELOG.md | 5 ++ jest.config.js | 3 +- package.json | 3 +- src/components/input/index.ts | 1 + src/components/input/inputDate/index.ts | 1 + .../input/inputDate/inputDate.stories.tsx | 25 +++++++++ .../input/inputDate/inputDate.test.tsx | 51 +++++++++++++++++++ src/components/input/inputDate/inputDate.tsx | 46 +++++++++++++++++ .../input/inputSearch/inputSearch.stories.tsx | 2 +- src/components/input/useInputProps.ts | 2 +- src/styles/primitives/colors.css | 3 ++ tailwind.config.js | 2 + yarn.lock | 5 ++ 13 files changed, 145 insertions(+), 4 deletions(-) create mode 100644 src/components/input/inputDate/index.ts create mode 100644 src/components/input/inputDate/inputDate.stories.tsx create mode 100644 src/components/input/inputDate/inputDate.test.tsx create mode 100644 src/components/input/inputDate/inputDate.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 831350311..5c9614f05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Fixed - Minimum `tailwindcss` version required +- Fix disabled input style on Firefox + +### Added + +- Implement `InputDate` component ## [1.0.8] - 2024-01-17 diff --git a/jest.config.js b/jest.config.js index aae6dfa82..bae6d85d8 100644 --- a/jest.config.js +++ b/jest.config.js @@ -8,8 +8,9 @@ const config = { setupFilesAfterEnv: ['/src/test/setup.ts'], transform: { '^.+\\.svg$': '/src/test/svgTransform.js', - '^.+\\.tsx?$': 'ts-jest', + '^.+\\.m?[tj]sx?$': 'ts-jest', }, + transformIgnorePatterns: ['node_modules/(?!(.*\\.mjs$|react-merge-refs))'], }; module.exports = config; diff --git a/package.json b/package.json index 9bc53a153..0b85260cc 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,8 @@ "@radix-ui/react-toggle-group": "^1.0.0", "classnames": "^2.0.0", "react": "^18.0.0", - "react-dom": "^18.0.0" + "react-dom": "^18.0.0", + "react-merge-refs": "^2.0.0" }, "peerDependencies": { "tailwindcss": "^3.4.0" diff --git a/src/components/input/index.ts b/src/components/input/index.ts index f95396202..c2a1910e1 100644 --- a/src/components/input/index.ts +++ b/src/components/input/index.ts @@ -1,3 +1,4 @@ export * from './inputContainer'; +export * from './inputDate'; export * from './inputSearch'; export * from './inputText'; diff --git a/src/components/input/inputDate/index.ts b/src/components/input/inputDate/index.ts new file mode 100644 index 000000000..a3251a039 --- /dev/null +++ b/src/components/input/inputDate/index.ts @@ -0,0 +1 @@ +export { InputDate, type IInputDateProps } from './inputDate'; diff --git a/src/components/input/inputDate/inputDate.stories.tsx b/src/components/input/inputDate/inputDate.stories.tsx new file mode 100644 index 000000000..f4f9b6330 --- /dev/null +++ b/src/components/input/inputDate/inputDate.stories.tsx @@ -0,0 +1,25 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { InputDate } from './inputDate'; + +const meta: Meta = { + title: 'components/Input/InputDate', + component: InputDate, + tags: ['autodocs'], + parameters: { + design: { + type: 'figma', + url: 'https://www.figma.com/file/jfKRr1V9evJUp1uBeyP3Zz/v1.0.0?type=design&node-id=10080-1466&mode=design&t=2bLCEeKZ7ueBboTs-4', + }, + }, +}; + +type Story = StoryObj; + +/** + * Default usage example of the InputDate component. + */ +export const Default: Story = { + args: {}, +}; + +export default meta; diff --git a/src/components/input/inputDate/inputDate.test.tsx b/src/components/input/inputDate/inputDate.test.tsx new file mode 100644 index 000000000..542ac58b8 --- /dev/null +++ b/src/components/input/inputDate/inputDate.test.tsx @@ -0,0 +1,51 @@ +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 { InputDate, type IInputDateProps } from './inputDate'; + +describe(' component', () => { + const useRefMock = jest.spyOn(React, 'useRef'); + const mergeRefMock = jest.spyOn(MergeRefs, 'mergeRefs'); + + afterEach(() => { + useRefMock.mockReset(); + mergeRefMock.mockReset(); + }); + + const createTestComponent = (props?: Partial) => { + const completeProps = { ...props }; + + return ; + }; + + it('renders a date input', () => { + const label = 'Date label'; + render(createTestComponent({ label })); + const dateInput = screen.getByLabelText(label); + expect(dateInput).toBeInTheDocument(); + expect(dateInput.type).toEqual('date'); + }); + + it('renders the input as disabled when the isDisabled property is set to true', () => { + const isDisabled = true; + const label = 'test'; + render(createTestComponent({ label, isDisabled })); + expect(screen.getByLabelText(label)).toBeDisabled(); + expect(screen.getByRole('button')).toBeDisabled(); + }); + + it('renders a button which opens the date picker on click', () => { + const showPicker = jest.fn(); + useRefMock.mockReturnValue({ current: { showPicker } }); + mergeRefMock.mockReturnValue(() => null); + render(createTestComponent()); + + const calendarButton = screen.getByRole('button'); + expect(calendarButton).toBeInTheDocument(); + expect(within(calendarButton).getByTestId(IconType.CALENDAR)).toBeInTheDocument(); + + fireEvent.click(calendarButton); + expect(showPicker).toHaveBeenCalled(); + }); +}); diff --git a/src/components/input/inputDate/inputDate.tsx b/src/components/input/inputDate/inputDate.tsx new file mode 100644 index 000000000..cc524a4f3 --- /dev/null +++ b/src/components/input/inputDate/inputDate.tsx @@ -0,0 +1,46 @@ +import classNames from 'classnames'; +import { forwardRef, useRef } from 'react'; +import { mergeRefs } from 'react-merge-refs'; +import { Button } from '../../button'; +import { IconType } from '../../icon'; +import { InputContainer, type IInputComponentProps } from '../inputContainer'; +import { useInputProps } from '../useInputProps'; + +export interface IInputDateProps extends Omit {} + +export const InputDate: React.FC = forwardRef((props, ref) => { + const { containerProps, inputProps } = useInputProps(props); + + const { className: containerClassName, ...otherContainerProps } = containerProps; + const { className: inputClassName, disabled, ...otherInputProps } = inputProps; + + const inputRef = useRef(null); + + const handleCalendarClick = () => { + inputRef.current?.showPicker(); + }; + + return ( + // Using absolute and relative positions to hide native date-picker icon on Firefox as it cannot be customised + // (see Firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1812397) + + +