diff --git a/README.md b/README.md index 7eb116d0..05d5d802 100644 --- a/README.md +++ b/README.md @@ -17,16 +17,19 @@ It supports most props of Form.Input and Dayzed components. Check the [supported -- [Installation](#installation) -- [Usage](#usage) -- [Supported Props](#supported-props) - - [Own Props](#own-props) - - [Form.Input Props](#forminput-props) - - [Dayzed Props](#dayzed-props) -- [Customization](#customization) -- [Roadmap](#roadmap) -- [Contributors](#contributors) -- [LICENSE](#license) +- [📆 react-semantic-ui-datepickers](#-react-semantic-ui-datepickers) + - [Overview](#overview) + - [Table of Contents](#table-of-contents) + - [Installation](#installation) + - [Usage](#usage) + - [Supported Props](#supported-props) + - [Own Props](#own-props) + - [Form.Input Props](#forminput-props) + - [Dayzed Props](#dayzed-props) + - [Customization](#customization) + - [Roadmap](#roadmap) + - [Contributors](#contributors) + - [LICENSE](#license) @@ -70,21 +73,24 @@ More examples [here](https://react-semantic-ui-datepickers.now.sh). ### Own Props -| property | type | required | default | description | -| -------------------- | -------- | -------- | ------------ | --------------------------------------------------------------------------------------------------------------- | -| allowOnlyNumbers | boolean | no | true | Allows the user enter only numbers | -| clearOnSameDateClick | boolean | no | true | Controls whether the datepicker's state resets if the same date is selected in succession. | -| clearable | boolean | no | true | Allows the user to clear the value | -| filterDate | function | no | () => true | Function that receives each date and returns a boolean to indicate whether it is enabled or not | -| format | string | no | 'YYYY-MM-DD' | Specifies how the date will be formatted using the [date-fns' format](https://date-fns.org/v1.29.0/docs/format) | -| keepOpenOnClear | boolean | no | false | Keeps the datepicker open (or opens it if it's closed) when the clear icon is clicked | -| keepOpenOnSelect | boolean | no | false | Keeps the datepicker open when a date is selected | -| locale | string | no | 'en-US' | Filename of the locale to be used. PS: Feel free to submit PR's with more locales! | -| onBlur | function | no | () => {} | Callback fired when the input loses focus | -| onChange | function | no | () => {} | Callback fired when the value changes | -| pointing | string | no | 'left' | Location to render the component around input. Available options: 'left', 'right', 'top left', 'top right' | -| type | string | no | basic | Type of input to render. Available options: 'basic' and 'range' | -| datePickerOnly | boolean | no | false | Allows the date to be selected only via the date picker and disables the text input | +| property | type | required | default | description | +| -------------------- | ------------ | -------- | ------------ | --------------------------------------------------------------------------------------------------------------- | +| allowOnlyNumbers | boolean | no | true | Allows the user enter only numbers | +| autoComplete | string | no | -- | Specifies if the input should have autocomplete enabled | +| clearOnSameDateClick | boolean | no | true | Controls whether the datepicker's state resets if the same date is selected in succession. | +| clearable | boolean | no | true | Allows the user to clear the value | +| filterDate | function | no | () => true | Function that receives each date and returns a boolean to indicate whether it is enabled or not | +| format | string | no | 'YYYY-MM-DD' | Specifies how the date will be formatted using the [date-fns' format](https://date-fns.org/v1.29.0/docs/format) | +| keepOpenOnClear | boolean | no | false | Keeps the datepicker open (or opens it if it's closed) when the clear icon is clicked | +| keepOpenOnSelect | boolean | no | false | Keeps the datepicker open when a date is selected | +| inline | boolean | no | false | Uses an inline variant, without the input and the features related to it, e.g. clearable datepicker | +| locale | string | no | 'en-US' | Filename of the locale to be used. PS: Feel free to submit PR's with more locales! | +| onBlur | function | no | () => {} | Callback fired when the input loses focus | +| onChange | function | no | () => {} | Callback fired when the value changes | +| pointing | string | no | 'left' | Location to render the component around input. Available options: 'left', 'right', 'top left', 'top right' | +| type | string | no | basic | Type of input to render. Available options: 'basic' and 'range' | +| datePickerOnly | boolean | no | false | Allows the date to be selected only via the date picker and disables the text input | +| value | Date\|Date[] | no | -- | The value of the datepicker | ### Form.Input Props @@ -129,7 +135,6 @@ In order to customize the elements, you can override the styles of the classes b - Improve accessibility > @donysukardi did some work on accessibility in the BaseDatePicker, but I couldn't get it working correcly. Feel free to help on this! - Add more tests (including e2e) - > The current threshold is pretty useless 😕 > Feel free to open issues and/or create PRs to improve other aspects of the library! diff --git a/src/__tests__/datepicker.test.tsx b/src/__tests__/datepicker.test.tsx index e4a41110..2e99c5c6 100644 --- a/src/__tests__/datepicker.test.tsx +++ b/src/__tests__/datepicker.test.tsx @@ -6,8 +6,9 @@ import localePt from '../locales/pt-BR.json'; import { getShortDate } from '../utils'; import DatePicker from '../'; -const setup = (props?: Partial) => { +const setup = (props: Partial = {}) => { const options = render(); + const getQuery = props.inline ? options.queryByTestId : options.getByTestId; return { ...options, @@ -20,8 +21,8 @@ const setup = (props?: Partial) => { options.rerender( ), - datePickerInput: options.getByTestId('datepicker-input') - .firstChild as HTMLInputElement, + datePickerInput: getQuery('datepicker-input') + ?.firstChild as HTMLInputElement, }; }; const onBlur = jest.fn(); @@ -37,10 +38,6 @@ afterEach(() => { }); describe('Basic datepicker', () => { - it('renders', () => { - expect(setup()).toBeTruthy(); - }); - describe('reacts to keyboard events', () => { it('closes datepicker on Esc', async () => { const { getByText, openDatePicker, queryByText } = setup({ onBlur }); @@ -299,10 +296,6 @@ describe('Basic datepicker', () => { }); describe('Range datepicker', () => { - it('renders', () => { - expect(setup({ type: 'range' })).toBeTruthy(); - }); - describe('reacts to keyboard events', () => { it('accepts valid input followed by Enter key', async () => { const { datePickerInput } = setup({ onBlur, type: 'range' }); @@ -435,3 +428,64 @@ describe('Range datepicker', () => { expect(datePickerInput.value).toBe(''); }); }); + +describe('Inline datepicker', () => { + it('should not display the input when inline prop is true', () => { + const { queryByTestId } = setup({ inline: true }); + + expect(queryByTestId('datepicker-input')).toBeFalsy(); + }); + + describe('basic variant', () => { + it('fires onChange with event and selected date as arguments', async () => { + const onChange = jest.fn(); + const today = getShortDate(new Date()) as string; + const { getByTestId } = setup({ inline: true, onChange }); + const todayCell = getByTestId(RegExp(today)); + + fireEvent.click(todayCell); + + expect(onChange).toHaveBeenNthCalledWith( + 1, + expect.any(Object), + expect.objectContaining({ + value: expect.any(Date), + }) + ); + }); + }); + + describe('range variant', () => { + it('fires onChange with event and selected dates as arguments', async () => { + const onChange = jest.fn(); + const now = new Date(); + const today = getShortDate(now) as string; + const tomorrow = getShortDate( + new Date(now.setDate(now.getDate() + 1)) + ) as string; + const { getByTestId } = setup({ onChange, inline: true, type: 'range' }); + const todayCell = getByTestId(RegExp(today)); + const tomorrowCell = getByTestId(RegExp(tomorrow)); + + fireEvent.click(todayCell); + + expect(onChange).toHaveBeenNthCalledWith( + 1, + expect.any(Object), + expect.objectContaining({ + value: [expect.any(Date)], + }) + ); + + fireEvent.click(tomorrowCell); + + expect(onChange).toHaveBeenNthCalledWith( + 2, + expect.any(Object), + expect.objectContaining({ + value: [expect.any(Date), expect.any(Date)], + }) + ); + }); + }); +}); diff --git a/src/components/calendar/calendar.css b/src/components/calendar/calendar.css index b64a805b..35f60c08 100644 --- a/src/components/calendar/calendar.css +++ b/src/components/calendar/calendar.css @@ -1,7 +1,11 @@ .clndr-calendars-segment { text-align: center; - position: absolute !important; + margin-bottom: 0.25rem !important; margin-top: 0.25rem !important; +} + +.clndr-calendars-segment.clndr-floating { + position: absolute !important; z-index: 2000; } diff --git a/src/components/calendar/calendar.tsx b/src/components/calendar/calendar.tsx index 8b9287e9..d807ba8d 100644 --- a/src/components/calendar/calendar.tsx +++ b/src/components/calendar/calendar.tsx @@ -14,6 +14,7 @@ type CalendarProps = { getBackProps: (props: any) => void; getDateProps: (props: any) => void; getForwardProps: (props: any) => void; + inline: SemanticDatepickerProps['inline']; maxDate?: Date; minDate?: Date; months: Locale['months']; @@ -45,6 +46,7 @@ const Calendar: React.FC = ({ getBackProps, getDateProps, getForwardProps, + inline, maxDate, minDate, months, @@ -57,7 +59,12 @@ const Calendar: React.FC = ({ weekdays, pointing, }) => ( - +
+ {(props) => ( + + )} + + ); - return ( + return inline ? ( + datepickerComponent + ) : (
- {isVisible && ( - - {(props) => ( - - )} - - )} + {isVisible && datepickerComponent}
); } diff --git a/src/types/index.ts b/src/types/index.ts index 178f2cb2..7e382a11 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -63,6 +63,7 @@ export type SemanticDatepickerProps = PickedDayzedProps & format: string; keepOpenOnClear: boolean; keepOpenOnSelect: boolean; + inline: boolean; locale: LocaleOptions; onBlur: (event?: React.SyntheticEvent) => void; onChange: ( diff --git a/stories/common.tsx b/stories/common.tsx index 4bc5b471..05429742 100644 --- a/stories/common.tsx +++ b/stories/common.tsx @@ -8,6 +8,7 @@ export const Content: React.FC = ({ children, style }) => (
{ const type = select('Type', typeMap, typeMap.basic); + const inline = boolean('Inline (without input)', false); const allowOnlyNumbers = boolean('Allow only numbers', false); const clearOnSameDateClick = boolean('Clear on same date click', true); const datePickerOnly = boolean('Datepicker only', false); @@ -71,6 +72,7 @@ stories.add('Basic usage', () => { format={format} keepOpenOnClear={keepOpenOnClear} keepOpenOnSelect={keepOpenOnSelect} + inline={inline} locale={locale} maxDate={maxDate} minDate={minDate}