@hyphen/hyphen-components 6.15.1 → 7.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/components/Calendar/Calendar.d.ts +5 -0
  2. package/dist/components/Calendar/Calendar.stories.d.ts +12 -0
  3. package/dist/css/index.css +2 -3
  4. package/dist/css/utilities.css +8 -0
  5. package/dist/hyphen-components.cjs.development.js +644 -809
  6. package/dist/hyphen-components.cjs.development.js.map +1 -1
  7. package/dist/hyphen-components.cjs.production.min.js +1 -1
  8. package/dist/hyphen-components.cjs.production.min.js.map +1 -1
  9. package/dist/hyphen-components.esm.js +645 -808
  10. package/dist/hyphen-components.esm.js.map +1 -1
  11. package/dist/index.d.ts +1 -2
  12. package/package.json +2 -3
  13. package/src/components/Calendar/Calendar.mdx +28 -0
  14. package/src/components/Calendar/Calendar.stories.tsx +217 -0
  15. package/src/components/Calendar/Calendar.tsx +117 -0
  16. package/src/components/Formik/Formik.stories.tsx +10 -21
  17. package/src/index.ts +1 -2
  18. package/src/styles/utilities.scss +8 -0
  19. package/dist/components/DateInput/DateInput.d.ts +0 -57
  20. package/dist/components/DateInput/DateInput.stories.d.ts +0 -11
  21. package/dist/components/DatePicker/DatePicker.d.ts +0 -86
  22. package/dist/components/DatePicker/DatePicker.stories.d.ts +0 -13
  23. package/src/components/DateInput/DateInput.mdx +0 -61
  24. package/src/components/DateInput/DateInput.stories.tsx +0 -168
  25. package/src/components/DateInput/DateInput.test.tsx +0 -176
  26. package/src/components/DateInput/DateInput.tsx +0 -212
  27. package/src/components/DatePicker/DatePicker.mdx +0 -52
  28. package/src/components/DatePicker/DatePicker.module.scss +0 -603
  29. package/src/components/DatePicker/DatePicker.stories.tsx +0 -199
  30. package/src/components/DatePicker/DatePicker.test.tsx +0 -26
  31. package/src/components/DatePicker/DatePicker.tsx +0 -138
@@ -1,61 +0,0 @@
1
- import { Canvas, Meta, ArgTypes } from '@storybook/addon-docs/blocks';
2
- import { DateInput } from './DateInput';
3
- import * as Stories from './DateInput.stories';
4
-
5
- <Meta of={Stories} />
6
-
7
- # DateInput
8
-
9
- Use a DateInput to allow users to select a date close to today and place that date in an input field.
10
- Avoid using this component if the user needs to enter a date that is many years in the past (i.e. birthday) or future.
11
- For entering these types of dates we recommend using the `TextInput` component with a `date` mask. See an example here:
12
- [TextInput Date Example](?path=/docs/components-textinput-overview--with-date-mask)
13
-
14
- <Canvas isExpanded of={Stories.Basic} />
15
-
16
- ## Props
17
-
18
- <ArgTypes of={DateInput} />
19
-
20
- ## Default
21
-
22
- The DateInput is composed of 3 components. Each can be tailored based on their props. They are:
23
-
24
- 1. [DatePicker](?path=/docs/components-datepicker-overview--basic-example)
25
- 2. [Popover](?path=/docs/components-popover-overview--demo)
26
- 3. [TextInput](?path=/docs/components-textinput-overview--default-story)
27
-
28
- The `DateInput` uses sensible defaults for these to allow for very straightforward ergonomics that allow users to
29
- select dates with minimal work.
30
-
31
- <Canvas of={Stories.Default} />
32
-
33
- ## Date Range
34
-
35
- <Canvas of={Stories.DateRange} />
36
-
37
- ## With Min/Max dates
38
-
39
- <Canvas of={Stories.WithMinAndMaxDates} />
40
-
41
- ## Custom Date Format
42
-
43
- You can manipulate the format on the input field by using the `dateFormat` and `dateOptions`
44
- props. Under the hood our component uses the [date-fns `format` function](https://date-fns.org/v2.16.1/docs/format)
45
- to generate a string based on these options.
46
-
47
- <Canvas of={Stories.CustomDateFormat} />
48
-
49
- ## On using onBlur event with DateInput
50
-
51
- The input is technically blurred whenever the calendar popover is interacted but as far as the user
52
- is concerned they are still interacting with the DateInput. Please keep this in mind when
53
- triggering form validation after an onBlur event.
54
-
55
- Make sure to pass the `onBlur` prop to the input via `textInputProps`.
56
-
57
- <Canvas of={Stories.InputBlurEvent} />
58
-
59
- ## Component Design Tokens
60
-
61
- This component shares component design tokens with all form controls. For a complete list of tokens, see the [Theming Form Controls documentation](/docs/theming-form-controls--custom-theme-form).
@@ -1,168 +0,0 @@
1
- import React, { useState } from 'react';
2
- import type { Meta } from '@storybook/react-vite';
3
- import { DateInput } from './DateInput';
4
- import { Box } from '../Box/Box';
5
-
6
- const meta: Meta<typeof DateInput> = {
7
- title: 'Components/DateInput',
8
- component: DateInput,
9
- };
10
-
11
- export default meta;
12
-
13
- export const Basic = () => (
14
- <DateInput
15
- datePickerProps={{
16
- onChange() {},
17
- }}
18
- textInputProps={{
19
- id: 'exampleDateInput',
20
- name: 'selectDate',
21
- label: 'Select a Date',
22
- placeholder: 'e.g. 11/02/2020',
23
- }}
24
- />
25
- );
26
-
27
- export const Default = () => {
28
- const [selectedDate, setSelectedDate] = useState<Date | null | [Date, Date]>(
29
- null
30
- );
31
- const handleClear = () => {
32
- setSelectedDate(null);
33
- };
34
-
35
- return (
36
- <DateInput
37
- datePickerProps={{
38
- selected: selectedDate instanceof Date ? selectedDate : null,
39
- onChange: (date: Date | [Date, Date] | null) => setSelectedDate(date),
40
- }}
41
- textInputProps={{
42
- placeholder: 'e.g. 11/02/2020',
43
- onClear: handleClear,
44
- id: 'defaultDatePicker',
45
- name: 'selectDate',
46
- label: 'Select Date',
47
- }}
48
- />
49
- );
50
- };
51
-
52
- export const DateRange = () => {
53
- const [startDate, setStartDate] = useState<Date | [Date, Date] | null>(null);
54
- const [endDate, setEndDate] = useState<Date | [Date, Date] | null>(null);
55
- const setDate = ([startDate, endDate]: [Date, Date]) => {
56
- setStartDate(startDate);
57
- setEndDate(endDate);
58
- };
59
- const handleClear = () => {
60
- setStartDate(null);
61
- setEndDate(null);
62
- };
63
-
64
- return (
65
- <DateInput
66
- datePickerProps={{
67
- // @ts-ignore - Type compatibility with onChange.
68
- onChange: setDate,
69
- selected: startDate as Date,
70
- selectsRange: true,
71
- startDate: startDate as Date,
72
- endDate: endDate as Date,
73
- }}
74
- textInputProps={{
75
- onClear: handleClear,
76
- id: 'myDateRangePicker',
77
- name: 'myDateRangePicker',
78
- label: 'Select Date Range',
79
- }}
80
- />
81
- );
82
- };
83
-
84
- export const WithMinAndMaxDates = () => {
85
- const [selectedDate, setSelectedDate] = useState<Date | [Date, Date] | null>(
86
- null
87
- );
88
- const handleClear = () => {
89
- setSelectedDate(null);
90
- };
91
- const min = new Date(2022, 6, 18);
92
- min.setDate(min.getDate() - 10);
93
- const max = new Date(2022, 6, 18);
94
- max.setDate(max.getDate() + 10);
95
- return (
96
- <DateInput
97
- datePickerProps={{
98
- selected: selectedDate as Date,
99
- maxDate: max,
100
- minDate: min,
101
- onChange: setSelectedDate,
102
- }}
103
- textInputProps={{
104
- placeholder: 'e.g. 11/02/2020',
105
- onClear: handleClear,
106
- id: 'defaultDatePicker',
107
- name: 'selectDate',
108
- label: 'Select Date',
109
- }}
110
- />
111
- );
112
- };
113
-
114
- export const CustomDateFormat = () => {
115
- const [selectedDate, setSelectedDate] = useState<Date | null | [Date, Date]>(
116
- new Date('2020, 11, 3')
117
- );
118
- const handleClear = () => {
119
- setSelectedDate(null);
120
- };
121
- return (
122
- <Box gap="md">
123
- <DateInput
124
- dateFormat={'MMMM dd, yyyy'}
125
- datePickerProps={{
126
- selected: selectedDate as Date,
127
- onChange: setSelectedDate,
128
- }}
129
- textInputProps={{
130
- onClear: handleClear,
131
- id: 'withCustomDateFormat',
132
- name: 'selectDate',
133
- label: 'Select Date',
134
- }}
135
- />
136
- <Box>
137
- <p>
138
- Selected Date (as ISO String):{' '}
139
- {selectedDate ? (selectedDate as Date).toISOString() : null}
140
- </p>
141
- </Box>
142
- </Box>
143
- );
144
- };
145
-
146
- export const InputBlurEvent = () => {
147
- const [selectedDate, setSelectedDate] = useState<Date | [Date, Date] | null>(
148
- new Date('2020, 11, 3')
149
- );
150
- const handleTextInputBlur = () => {
151
- alert('TextInput Blur Event');
152
- };
153
- return (
154
- <DateInput
155
- dateFormat={'MMMM dd, yyyy'}
156
- datePickerProps={{
157
- selected: selectedDate as Date,
158
- onChange: setSelectedDate,
159
- }}
160
- textInputProps={{
161
- id: 'withCustomDateFormat',
162
- name: 'selectDate',
163
- label: 'Select Date',
164
- onBlur: handleTextInputBlur,
165
- }}
166
- />
167
- );
168
- };
@@ -1,176 +0,0 @@
1
- import React from 'react';
2
- import { render, screen, fireEvent, waitFor } from '@testing-library/react';
3
- import { DateInput } from './DateInput';
4
-
5
- describe('DateInput', () => {
6
- describe('Default', () => {
7
- it('renders a DateInput component with defaults', () => {
8
- render(
9
- <DateInput
10
- textInputProps={{
11
- id: 'myInput',
12
- label: 'Select Date',
13
- }}
14
- datePickerProps={{
15
- onChange: () => null,
16
- }}
17
- />
18
- );
19
-
20
- const input = screen.getByLabelText('Select Date');
21
- expect(input).toBeInTheDocument();
22
- });
23
- });
24
-
25
- describe('Popover', () => {
26
- it('keeps popover open after only start date is picked in a range', async () => {
27
- const date = new Date(2020, 0, 1);
28
- render(
29
- <DateInput
30
- textInputProps={{
31
- id: 'myInput',
32
- label: 'Select Date',
33
- }}
34
- datePickerProps={{
35
- onChange: () => null,
36
- openToDate: date,
37
- startDate: date,
38
- selected: date,
39
- selectsRange: true,
40
- endDate: null,
41
- }}
42
- />
43
- );
44
- const input = screen.getByLabelText('Select Date');
45
- fireEvent.click(input);
46
- const popoverContainer =
47
- document.getElementsByClassName('PopoverContent');
48
- await waitFor(() => expect(popoverContainer[0]).toBeInTheDocument());
49
- // Simulate picking start date (should not close popover)
50
- // (Assume date button is present)
51
- // Popover should still be open
52
- expect(document.getElementsByClassName('PopoverContent').length).toBe(1);
53
- });
54
-
55
- it('opens the Popover when the input is clicked', async () => {
56
- render(
57
- <DateInput
58
- textInputProps={{
59
- id: 'myInput',
60
- label: 'Select Date',
61
- }}
62
- datePickerProps={{
63
- onChange: () => null,
64
- }}
65
- />
66
- );
67
-
68
- const input = screen.getByLabelText('Select Date');
69
- fireEvent.click(input);
70
-
71
- const popoverContainer =
72
- document.getElementsByClassName('PopoverContent');
73
- await waitFor(() =>
74
- expect(popoverContainer[0]).toHaveAttribute('data-side', 'bottom')
75
- );
76
- await waitFor(() =>
77
- expect(popoverContainer[0]).toHaveAttribute('data-align', 'start')
78
- );
79
- });
80
- });
81
-
82
- describe('Date Formatting', () => {
83
- it('formats the date when a format is passed', async () => {
84
- const date = new Date(1995, 11, 14);
85
- render(
86
- <DateInput
87
- dateFormat="yyyy/MM/dd"
88
- textInputProps={{
89
- id: 'myInput',
90
- label: 'Select Date',
91
- }}
92
- datePickerProps={{
93
- onChange: () => null,
94
- openToDate: date,
95
- selected: date,
96
- }}
97
- />
98
- );
99
-
100
- const input = screen.getByLabelText('Select Date');
101
- expect(input).toBeInTheDocument();
102
- expect(input).toHaveAttribute('value', '1995/12/14');
103
- });
104
-
105
- it('formats both dates when range is being selected', async () => {
106
- const dateOne = new Date(1995, 11, 14);
107
- const dateTwo = new Date(1995, 11, 16);
108
- render(
109
- <DateInput
110
- dateFormat="yyyy/MM/dd"
111
- textInputProps={{
112
- id: 'myInput',
113
- label: 'Select Date',
114
- }}
115
- datePickerProps={{
116
- onChange: () => null,
117
- openToDate: dateOne,
118
- startDate: dateOne,
119
- endDate: dateTwo,
120
- selectsRange: true,
121
- }}
122
- />
123
- );
124
-
125
- const input = screen.getByLabelText('Select Date');
126
- expect(input).toBeInTheDocument();
127
- expect(input).toHaveAttribute('value', '1995/12/14 - 1995/12/16');
128
- });
129
-
130
- it('formats one date if selecting range', async () => {
131
- const dateOne = null;
132
- const dateTwo = new Date(1995, 11, 16);
133
- const { rerender } = render(
134
- <DateInput
135
- dateFormat="yyyy/MM/dd"
136
- textInputProps={{
137
- id: 'myInput',
138
- label: 'Select Date',
139
- }}
140
- datePickerProps={{
141
- onChange: () => null,
142
- openToDate: dateTwo,
143
- startDate: dateOne,
144
- endDate: dateTwo,
145
- selectsRange: true,
146
- }}
147
- />
148
- );
149
-
150
- const input = screen.getByLabelText('Select Date');
151
- expect(input).toBeInTheDocument();
152
- expect(input).toHaveAttribute('value', ' - 1995/12/16');
153
-
154
- rerender(
155
- <DateInput
156
- dateFormat="yyyy/MM/dd"
157
- textInputProps={{
158
- id: 'myInput',
159
- label: 'Select Date',
160
- }}
161
- datePickerProps={{
162
- onChange: () => null,
163
- openToDate: dateTwo,
164
- startDate: dateTwo,
165
- endDate: dateOne,
166
- selectsRange: true,
167
- }}
168
- />
169
- );
170
-
171
- const inputTwo = screen.getByLabelText('Select Date');
172
- expect(inputTwo).toBeInTheDocument();
173
- expect(inputTwo).toHaveAttribute('value', '1995/12/16 - ');
174
- });
175
- });
176
- });
@@ -1,212 +0,0 @@
1
- import React, { FC, useState } from 'react';
2
- import format from 'date-fns/format';
3
- import { DatePicker, DatePickerProps } from '../DatePicker/DatePicker';
4
- import { TextInput, TextInputProps } from '../TextInput/TextInput';
5
- import {
6
- Popover,
7
- PopoverContent,
8
- PopoverPortal,
9
- PopoverTrigger,
10
- } from '../Popover/Popover';
11
- import { useOpenClose } from '../../hooks';
12
-
13
- export interface DateInputProps {
14
- /**
15
- * Props object for DatePicker component.
16
- */
17
- datePickerProps: DatePickerProps;
18
- /**
19
- * Props object for TextInput component.
20
- */
21
- textInputProps: Omit<TextInputProps, 'onChange'>;
22
- /**
23
- * Format for final date to be displayed.
24
- * Relies on date-fns/format --> https://date-fns.org/v1.9.0/docs/format
25
- */
26
- dateFormat?: string;
27
- /**
28
- * Additional settings for formatting date.
29
- */
30
- dateOptions?: {
31
- /**
32
- * The user's locale.
33
- */
34
- locale?: globalThis.Locale | undefined;
35
- /**
36
- * Start of week.
37
- */
38
- weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined;
39
- /**
40
- * Should determine which week is week 1 of a new year.
41
- */
42
- firstWeekContainsDate?: number | undefined;
43
- /**
44
- * Whether to accept unicode tokens in format.
45
- * See here --> https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md
46
- */
47
- useAdditionalWeekYearTokens?: boolean | undefined;
48
- /**
49
- * Whether to accept unicode tokens in format.
50
- * See here --> https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md
51
- */
52
- useAdditionalDayOfYearTokens?: boolean | undefined;
53
- };
54
- /**
55
- * Props to pass down to the Popover component.
56
- */
57
- popoverProps?: {
58
- side: 'top' | 'bottom' | 'left' | 'right';
59
- align: 'start' | 'center' | 'end';
60
- };
61
- /**
62
- * Additional props to be spread to the `TextInput` element.
63
- */
64
- [x: string]: any; // eslint-disable-line
65
- }
66
-
67
- const defaultDatePickerProps: Omit<DatePickerProps, 'onChange'> = {
68
- selected: null,
69
- selectsRange: false,
70
- };
71
-
72
- const defaultPopoverProps = {
73
- align: 'start',
74
- side: 'bottom',
75
- };
76
-
77
- const defaultTextInputProps: Omit<TextInputProps, 'id'> = {
78
- label: 'Select Date',
79
- };
80
-
81
- export const DateInput: FC<DateInputProps> = ({
82
- datePickerProps,
83
- textInputProps,
84
- dateFormat = 'MM/dd/yyyy',
85
- dateOptions = undefined,
86
- popoverProps = { ...defaultPopoverProps },
87
- }) => {
88
- const { isOpen, handleClose, handleOpen } = useOpenClose();
89
-
90
- // Internal state for selected date if not controlled
91
- const isControlled = datePickerProps.selected !== undefined;
92
- const [internalSelected, setInternalSelected] = useState<Date | null>(
93
- Array.isArray(datePickerProps.selected)
94
- ? datePickerProps.selected[0] ?? null
95
- : datePickerProps.selected ?? null
96
- );
97
-
98
- // Use controlled or internal state
99
- const selectedDate = isControlled
100
- ? datePickerProps.selected
101
- : internalSelected;
102
-
103
- const mergedDatePickerProps = {
104
- ...defaultDatePickerProps,
105
- ...datePickerProps,
106
- selected: selectedDate,
107
- };
108
-
109
- const mergedPopoverProps = {
110
- ...defaultPopoverProps,
111
- ...popoverProps,
112
- side: (popoverProps?.side ?? defaultPopoverProps.side) as
113
- | 'top'
114
- | 'bottom'
115
- | 'left'
116
- | 'right',
117
- align: (popoverProps?.align ?? defaultPopoverProps.align) as
118
- | 'start'
119
- | 'center'
120
- | 'end',
121
- onInteractOutside: handleClose,
122
- };
123
-
124
- const mergedTextInputProps = {
125
- ...defaultTextInputProps,
126
- ...textInputProps,
127
- };
128
-
129
- const getTextInputValue = () => {
130
- const { selectsRange, startDate, endDate, selected } =
131
- mergedDatePickerProps;
132
- // If selectsRange and selected is an array, use it for start/end
133
- let rangeStart = startDate;
134
- let rangeEnd = endDate;
135
- if (selectsRange && Array.isArray(selected)) {
136
- rangeStart = selected[0] ?? null;
137
- rangeEnd = selected[1] ?? null;
138
- }
139
- const formattedStartDate = rangeStart
140
- ? format(rangeStart, dateFormat, dateOptions)
141
- : '';
142
- const formattedEndDate = rangeEnd
143
- ? format(rangeEnd, dateFormat, dateOptions)
144
- : '';
145
- const formattedSelectedDate =
146
- selected && !selectsRange && !Array.isArray(selected)
147
- ? format(selected, dateFormat, dateOptions)
148
- : '';
149
- if (selectsRange) {
150
- return `${formattedStartDate}${
151
- formattedStartDate || formattedEndDate ? ' - ' : ''
152
- }${formattedEndDate}`;
153
- }
154
- return formattedSelectedDate;
155
- };
156
-
157
- const handleDatePickerChange = (
158
- date: Date | [Date, Date] | null,
159
- event?: React.SyntheticEvent<any, Event>
160
- ) => {
161
- if (datePickerProps.onChange) {
162
- datePickerProps.onChange(date, event);
163
- }
164
- if (!isControlled) {
165
- // If not controlled, update internal state
166
- if (Array.isArray(date)) {
167
- setInternalSelected(date[0] ?? null);
168
- } else {
169
- setInternalSelected(date);
170
- }
171
- }
172
- // Close popover when a date is selected (single) or when end date is selected (range)
173
- if (mergedDatePickerProps.selectsRange) {
174
- if (Array.isArray(date) && date[0] && date[1]) {
175
- handleClose();
176
- }
177
- } else if (date) {
178
- handleClose();
179
- }
180
- };
181
-
182
- return (
183
- <Popover open={isOpen}>
184
- <PopoverTrigger asChild>
185
- <TextInput
186
- id={mergedTextInputProps.id}
187
- name={mergedTextInputProps.name}
188
- label={mergedTextInputProps.label}
189
- value={getTextInputValue()}
190
- readOnly
191
- onClick={handleOpen}
192
- inputProps={{ className: 'text-align-left' }}
193
- type="text"
194
- onChange={() =>
195
- null
196
- } /* Empty function since we hijack the onChange event */
197
- {...mergedTextInputProps}
198
- />
199
- </PopoverTrigger>
200
- <PopoverPortal>
201
- <PopoverContent {...mergedPopoverProps}>
202
- <DatePicker
203
- {...mergedDatePickerProps}
204
- onChange={handleDatePickerChange}
205
- selected={selectedDate}
206
- selectsRange={mergedDatePickerProps.selectsRange}
207
- />
208
- </PopoverContent>
209
- </PopoverPortal>
210
- </Popover>
211
- );
212
- };
@@ -1,52 +0,0 @@
1
- import { Canvas, Meta, ArgTypes } from '@storybook/addon-docs/blocks';
2
- import { DatePicker } from './DatePicker';
3
- import * as Stories from './DatePicker.stories';
4
-
5
- <Meta of={Stories} />
6
-
7
- # DatePicker
8
-
9
- The Datepicker lets users select a date by showing them a calendar.
10
-
11
- It is also used in combination with an input and popover by the [DateInput component](/?path=/docs/components-dateinput-overview--basic-usage).
12
-
13
- ### It can be used for:
14
-
15
- - selecting a single date
16
- - selecting a start and end date
17
-
18
- ## Props
19
-
20
- <ArgTypes of={DatePicker} />
21
-
22
- ## Basic Example
23
-
24
- <Canvas of={Stories.BasicExample} />
25
-
26
- ## Date Range
27
-
28
- <Canvas of={Stories.DateRange} />
29
-
30
- ## Min/Max Dates
31
-
32
- <Canvas of={Stories.MinAndMaxDates} />
33
-
34
- ## Month Picker
35
-
36
- <Canvas of={Stories.MonthPicker} />
37
-
38
- ## Show Multiple Months
39
-
40
- <Canvas of={Stories.ShowMultipleMonths} />
41
-
42
- ## With Time Picker
43
-
44
- <Canvas of={Stories.WithTimePicker} />
45
-
46
- ## Open by default on a specific date
47
-
48
- <Canvas of={Stories.OpenByDefaultOnASpecificDate} />
49
-
50
- ## With Children
51
-
52
- <Canvas of={Stories.WithChildren} />