-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(date-input): add component (#751)
- Loading branch information
Showing
15 changed files
with
514 additions
and
4 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{ | ||
"name": "@alfalab/core-components-date-input", | ||
"version": "1.0.0", | ||
"description": "", | ||
"keywords": [], | ||
"license": "MIT", | ||
"main": "dist/index.js", | ||
"module": "./dist/esm/index.js", | ||
"files": [ | ||
"dist" | ||
], | ||
"scripts": { | ||
"postinstall": "node ./dist/send-stats.js > /dev/null 2>&1 || exit 0" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"peerDependencies": { | ||
"react": "^16.9.0 || ^17.0.1" | ||
}, | ||
"dependencies": { | ||
"@alfalab/core-components-masked-input": "^4.0.1", | ||
"date-fns": "^2.16.1" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import React from 'react'; | ||
import userEvent from '@testing-library/user-event'; | ||
import { render, waitFor } from '@testing-library/react'; | ||
|
||
import { DateInput } from './index'; | ||
|
||
describe('DateInput', () => { | ||
describe('Display tests', () => { | ||
it('should match snapshot', () => { | ||
expect(render(<DateInput value='01.01.2021' />).container).toMatchSnapshot(); | ||
}); | ||
}); | ||
|
||
it('should set `data-test-id` attribute', () => { | ||
const testId = 'test-id'; | ||
const { getByTestId } = render(<DateInput dataTestId={testId} />); | ||
|
||
expect(getByTestId(testId)).toBeInTheDocument(); | ||
}); | ||
|
||
it('should set custom class', () => { | ||
const className = 'custom-class'; | ||
const { container } = render(<DateInput className={className} />); | ||
|
||
expect(container.firstElementChild).toHaveClass(className); | ||
}); | ||
|
||
it('should render input[type=date] if mobileMode=native', async () => { | ||
const { container } = render(<DateInput mobileMode='native' />); | ||
|
||
expect(container.querySelector('input[type="date"]')).toBeInTheDocument(); | ||
}); | ||
|
||
describe('Controlled-way', () => { | ||
it('should set value to input', () => { | ||
const value = '01.01.2020'; | ||
const value2 = '02.01.2020'; | ||
const { queryByRole, rerender } = render(<DateInput value={value} />); | ||
|
||
expect(queryByRole('textbox')).toHaveValue(value); | ||
|
||
rerender(<DateInput value={value2} />); | ||
|
||
expect(queryByRole('textbox')).toHaveValue(value2); | ||
}); | ||
}); | ||
|
||
describe('Uncontrolled-way', () => { | ||
it('should set default value to input', () => { | ||
const value = '01.01.2020'; | ||
const { queryByRole } = render(<DateInput defaultValue={value} />); | ||
|
||
expect(queryByRole('textbox')).toHaveValue(value); | ||
}); | ||
|
||
it('should set value to input', async () => { | ||
const value = '01.01.2020'; | ||
const { queryByRole } = render(<DateInput />); | ||
|
||
const input = queryByRole('textbox') as HTMLInputElement; | ||
|
||
userEvent.type(input, value); | ||
|
||
await waitFor(() => { | ||
expect(input).toHaveValue(value); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('Callback tests', () => { | ||
it('should call onChange callback', () => { | ||
const cb = jest.fn(); | ||
const value = '01.01.2020'; | ||
const { queryByRole } = render(<DateInput onChange={cb} />); | ||
|
||
const input = queryByRole('textbox') as HTMLInputElement; | ||
|
||
userEvent.type(input, value); | ||
|
||
expect(cb).toBeCalledTimes(value.length); | ||
}); | ||
}); | ||
|
||
describe('Render tests', () => { | ||
test('should unmount without errors', () => { | ||
const { unmount } = render(<DateInput />); | ||
|
||
expect(unmount).not.toThrowError(); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
import React, { ChangeEvent, useCallback, useMemo, useState } from 'react'; | ||
import { MaskedInput, MaskedInputProps } from '@alfalab/core-components-masked-input'; | ||
|
||
import { | ||
SUPPORTS_INPUT_TYPE_DATE, | ||
NATIVE_DATE_FORMAT, | ||
createAutoCorrectedDatePipe, | ||
parseDateString, | ||
formatDate, | ||
mask, | ||
} from './utils'; | ||
|
||
import styles from './index.module.css'; | ||
|
||
export type DateInputProps = Omit<MaskedInputProps, 'onBeforeDisplay' | 'mask' | 'onChange'> & { | ||
/** | ||
* Минимальный год, доступный для ввода | ||
*/ | ||
minYear?: number; | ||
|
||
/** | ||
* Максимальный год, доступный для ввода | ||
*/ | ||
maxYear?: number; | ||
|
||
/** | ||
* Управление нативным режимом на мобильных устройствах | ||
*/ | ||
mobileMode?: 'input' | 'native'; | ||
|
||
/** | ||
* Обработчик изменения значения | ||
*/ | ||
onChange?: ( | ||
event: ChangeEvent<HTMLInputElement>, | ||
payload: { date: Date; value: string }, | ||
) => void; | ||
}; | ||
|
||
export const DateInput = React.forwardRef<HTMLInputElement, DateInputProps>( | ||
( | ||
{ | ||
maxYear, | ||
minYear, | ||
mobileMode = 'input', | ||
value, | ||
defaultValue, | ||
rightAddons, | ||
onChange, | ||
...restProps | ||
}, | ||
ref, | ||
) => { | ||
const uncontrolled = value === undefined; | ||
const shouldRenderNative = SUPPORTS_INPUT_TYPE_DATE && mobileMode === 'native'; | ||
|
||
const [stateValue, setStateValue] = useState(defaultValue); | ||
|
||
const inputValue = uncontrolled ? stateValue : value; | ||
|
||
const pipe = useMemo( | ||
() => | ||
createAutoCorrectedDatePipe({ | ||
maxYear, | ||
minYear, | ||
}), | ||
[maxYear, minYear], | ||
); | ||
|
||
const changeHandler = useCallback( | ||
(event: ChangeEvent<HTMLInputElement>, newValue: string, newDate: Date) => { | ||
if (uncontrolled) { | ||
setStateValue(newValue); | ||
} | ||
|
||
if (onChange) { | ||
onChange(event, { date: newDate, value: newValue }); | ||
} | ||
}, | ||
[onChange, uncontrolled], | ||
); | ||
|
||
const handleChange = useCallback( | ||
(event: ChangeEvent<HTMLInputElement>) => { | ||
const newValue = event.target.value; | ||
const newDate = parseDateString(newValue); | ||
|
||
changeHandler(event, newValue, newDate); | ||
}, | ||
[changeHandler], | ||
); | ||
|
||
const handleNativeInputChange = useCallback( | ||
(event: ChangeEvent<HTMLInputElement>) => { | ||
const newDate = parseDateString(event.target.value, NATIVE_DATE_FORMAT); | ||
const newValue = event.target.value === '' ? '' : formatDate(newDate); | ||
|
||
changeHandler(event, newValue, newDate); | ||
}, | ||
[changeHandler], | ||
); | ||
|
||
return ( | ||
<MaskedInput | ||
{...restProps} | ||
ref={ref} | ||
mask={mask} | ||
keepCharPositions={true} | ||
defaultValue={defaultValue} | ||
value={inputValue} | ||
onBeforeDisplay={pipe} | ||
onChange={handleChange} | ||
rightAddons={ | ||
<React.Fragment> | ||
<span className={styles.icon} /> | ||
{rightAddons} | ||
{shouldRenderNative && ( | ||
<input | ||
type='date' | ||
ref={ref} | ||
defaultValue={defaultValue} | ||
onChange={handleNativeInputChange} | ||
className={styles.nativeInput} | ||
/> | ||
)} | ||
</React.Fragment> | ||
} | ||
/> | ||
); | ||
}, | ||
); |
35 changes: 35 additions & 0 deletions
35
packages/date-input/src/__snapshots__/Component.test.tsx.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`DateInput Display tests should match snapshot 1`] = ` | ||
<div> | ||
<div | ||
class="component s hasRightAddons" | ||
> | ||
<div | ||
class="inner filled" | ||
> | ||
<div | ||
class="inputWrapper" | ||
> | ||
<div | ||
class="input" | ||
> | ||
<input | ||
class="input" | ||
type="text" | ||
value="01.01.2021" | ||
/> | ||
</div> | ||
</div> | ||
<div | ||
class="addons rightAddons" | ||
> | ||
<span | ||
class="icon" | ||
/> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { Meta, Props, Story } from '@storybook/addon-docs/blocks'; | ||
import { text, select, boolean } from '@storybook/addon-knobs'; | ||
import { ComponentHeader, Tabs, CssVars } from 'storybook/blocks'; | ||
|
||
import { DateInput } from '../Component'; | ||
import { name, version } from '../../package.json'; | ||
import Description from './description.mdx'; | ||
import Changelog from '../../CHANGELOG.md'; | ||
import styles from '!!raw-loader!../index.module.css'; | ||
|
||
|
||
<Meta | ||
title='Компоненты/DateInput' | ||
component={DateInput} | ||
parameters={{ 'theme-switcher': { themes: ['click', 'mobile'] }, }} | ||
/> | ||
|
||
|
||
<!-- Canvas --> | ||
|
||
<Story name='DateInput'> | ||
<DateInput | ||
block={boolean('block', false)} | ||
size={select('size', ['s', 'm', 'l', 'xl'], 's')} | ||
error={text('error', '')} | ||
hint={text('hint', '')} | ||
label={text('label', '')} | ||
/> | ||
</Story> | ||
|
||
|
||
<!-- Docs --> | ||
|
||
<ComponentHeader | ||
name='DateInput' | ||
version={version} | ||
package='@alfalab/core-components/date-input' | ||
stage={1} | ||
/> | ||
|
||
```tsx | ||
import { DateInput } from '@alfalab/core-components/date-input'; | ||
``` | ||
|
||
|
||
<Tabs | ||
description={<Description />} | ||
props={<Props of={DateInput} />} | ||
changelog={<Changelog />} | ||
cssVars={<CssVars css={styles} />} | ||
/> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
```tsx live | ||
<DateInput /> | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
@import '../../themes/src/default.css'; | ||
|
||
:root { | ||
--date-input-icon: url('https://alfabank.st/icons/glyph_calendar_m.svg'); | ||
} | ||
|
||
.icon { | ||
width: 24px; | ||
height: 24px; | ||
display: block; | ||
background: var(--date-input-icon); | ||
background-size: cover; | ||
background-position: center; | ||
} | ||
|
||
.nativeInput { | ||
opacity: 0; | ||
position: absolute; | ||
top: 0; | ||
left: 0; | ||
width: 100%; | ||
height: 100%; | ||
appearance: none; | ||
z-index: 1; | ||
|
||
&::-webkit-calendar-picker-indicator { | ||
display: none; | ||
} | ||
&::-webkit-inner-spin-button { | ||
display: none; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './Component'; |
Oops, something went wrong.