Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
fd2fbce
calendar phase 1
aresnik11 Mar 2, 2026
0ffb424
add focus management
aresnik11 Mar 4, 2026
b8a856d
add input
aresnik11 Mar 4, 2026
c096fe9
datepicker
aresnik11 Mar 4, 2026
9346ac9
clean up
aresnik11 Mar 5, 2026
00a8584
refactor datepicker
aresnik11 Mar 6, 2026
1946d73
clean up hooks
aresnik11 Mar 6, 2026
39d0de0
update exports
aresnik11 Mar 9, 2026
23f593d
add missing validation file
aresnik11 Mar 9, 2026
c1c9245
clean up inputref
aresnik11 Mar 9, 2026
9a8f70d
wip fix for typing date
aresnik11 Mar 10, 2026
82854c8
cleaning up
aresnik11 Mar 11, 2026
4f39ff9
update range logic
aresnik11 Mar 12, 2026
d194237
range logic when specific input is focused
aresnik11 Mar 12, 2026
3bbeb01
deselect logic
aresnik11 Mar 12, 2026
a6cbe80
fix lint
aresnik11 Mar 12, 2026
a56426f
fix story
aresnik11 Mar 12, 2026
f79ce4a
fix lint again
aresnik11 Mar 12, 2026
e0ee942
update snapshot
aresnik11 Mar 12, 2026
a417554
fix lint
aresnik11 Mar 12, 2026
cf4a438
show 2 months on larger screens
aresnik11 Mar 13, 2026
d3f6fac
style updates
aresnik11 Mar 13, 2026
e615f9f
PR feedback
aresnik11 Mar 17, 2026
a19fc7b
fix today in range color
aresnik11 Mar 17, 2026
9c30d34
clean up context
aresnik11 Mar 17, 2026
b657556
update placeholder text to be locale based
aresnik11 Mar 17, 2026
1924433
update next/last month text to be locale based
aresnik11 Mar 17, 2026
a83a99a
capitalize
aresnik11 Mar 17, 2026
40a0c8b
Merge branch 'main' into ajr-datepicker-styles
aresnik11 Mar 17, 2026
bfd8206
translations
aresnik11 Mar 17, 2026
f6463b4
add formgroup
aresnik11 Mar 17, 2026
7de8b27
Merge branch 'ajr-datepicker-styles' into ajr-datepicker-localization
aresnik11 Mar 17, 2026
c9a7576
fix calendar alignment and add shadow
aresnik11 Mar 17, 2026
d3f6fba
clear button logic
aresnik11 Mar 17, 2026
2834206
more translations
aresnik11 Mar 18, 2026
b436daa
fix disabled hover
aresnik11 Mar 18, 2026
5b7a8be
make disabledDates optional and add to story
aresnik11 Mar 18, 2026
496e740
disabled date in range logic
aresnik11 Mar 18, 2026
5df9d1a
fix keyboard nav now that showing 2 months
aresnik11 Mar 18, 2026
38f81a9
PR feedback
aresnik11 Mar 18, 2026
b9aa903
toLocaleUpperCase
aresnik11 Mar 18, 2026
b400a75
small fixes
aresnik11 Mar 19, 2026
0e10748
fix range styling
aresnik11 Mar 19, 2026
dfdabe8
fix alignments
aresnik11 Mar 23, 2026
1ee01fc
rename props
aresnik11 Mar 23, 2026
a6c8388
move around helpers
aresnik11 Mar 23, 2026
3bfab02
fix calendar story
aresnik11 Mar 23, 2026
afaea71
support small input size
aresnik11 Mar 23, 2026
e4e15be
initial focus a11y updates
aresnik11 Mar 25, 2026
422e220
PR feedback
aresnik11 Mar 25, 2026
8072692
clean up utils
aresnik11 Mar 26, 2026
ca9a66b
add full date aria label to date cells
aresnik11 Mar 26, 2026
13d1a3d
move around files and clean up exports
aresnik11 Mar 26, 2026
6d9082a
update locale
aresnik11 Mar 26, 2026
15ceaf4
week starts on from locale + polyfill
aresnik11 Mar 27, 2026
2effd5f
remove button from DateCell
aresnik11 Mar 27, 2026
e6d8f15
refactor CalendarHeader
aresnik11 Mar 27, 2026
af18ad7
Merge branch 'main' into ajr-datepicker-localization
aresnik11 Mar 30, 2026
d189e1c
fix errors
aresnik11 Mar 30, 2026
4df63bb
fix ci errors
aresnik11 Mar 30, 2026
5005ee6
Merge branch 'main' into ajr-datepicker-localization
aresnik11 Mar 31, 2026
23458a7
input segments
aresnik11 Apr 6, 2026
bab532a
fix segments typing
aresnik11 Apr 6, 2026
8f4c3c2
Merge branch 'main' into ajr-datepicker-localization
aresnik11 Apr 6, 2026
dc273f3
dedupe and format
aresnik11 Apr 6, 2026
43007af
version plan
aresnik11 Apr 6, 2026
aa6748d
fix sb import
aresnik11 Apr 6, 2026
cd040bb
tests round 1
aresnik11 Apr 6, 2026
e086026
CalendarBody tests
aresnik11 Apr 6, 2026
3b676f2
style updates
aresnik11 Apr 7, 2026
878339f
utils tests
aresnik11 Apr 7, 2026
c87e08d
segmentUtils tests
aresnik11 Apr 8, 2026
e8fe147
segment tests
aresnik11 Apr 8, 2026
0394f4b
fix segment refs
aresnik11 Apr 9, 2026
1d7671a
close on date select
aresnik11 Apr 9, 2026
ed8e59c
fix focus & active range part
aresnik11 Apr 9, 2026
6431e61
move DatePicker to organism
aresnik11 Apr 10, 2026
464b82a
WIP mdx file
aresnik11 Apr 10, 2026
533625b
quick actions
aresnik11 Apr 13, 2026
32ce0ff
evan pr feedback
aresnik11 Apr 13, 2026
e4b9092
dont render empty border in footer
aresnik11 Apr 13, 2026
1cc29cd
first passthrough of PR feedback
aresnik11 Apr 14, 2026
decc8ec
reorg files
aresnik11 Apr 15, 2026
5283400
update disabled date logic
aresnik11 Apr 15, 2026
cedb664
fix icon button tip sticking around
aresnik11 Apr 15, 2026
e8d2435
discriminated union context
aresnik11 Apr 15, 2026
589f1e5
change to object syntax
aresnik11 Apr 15, 2026
0e746bc
PR feedback
aresnik11 Apr 16, 2026
de8c1f6
fix date keyboard nav
aresnik11 Apr 16, 2026
dda6e47
more tests & tweaks
aresnik11 Apr 16, 2026
5fbe2b9
more tests
aresnik11 Apr 16, 2026
94c7a2d
sync segment input and calendar month shown
aresnik11 Apr 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .nx/version-plans/version-plan-1775495905156.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
gamut: minor
---

New DatePicker component
14 changes: 13 additions & 1 deletion packages/gamut/__tests__/__snapshots__/gamut.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing

exports[`Gamut Exported Keys 1`] = `
[
Expand All @@ -14,6 +14,12 @@ exports[`Gamut Exported Keys 1`] = `
"BodyPortal",
"Box",
"Breadcrumbs",
"CalendarBody",
"CalendarFooter",
"CalendarHeader",
"CalendarNavLastMonth",
"CalendarNavNextMonth",
"CalendarWrapper",
"Card",
"Checkbox",
"Coachmark",
Expand All @@ -33,6 +39,11 @@ exports[`Gamut Exported Keys 1`] = `
"CTAButton",
"DataList",
"DataTable",
"DatePicker",
"DatePickerCalendar",
"DatePickerContext",
"DatePickerInput",
"DatePickerProvider",
"DelayedRenderWrapper",
Comment on lines 17 to 47
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we want all of these exported? seems like some are internal components

"Dialog",
"Disclosure",
Expand Down Expand Up @@ -113,6 +124,7 @@ exports[`Gamut Exported Keys 1`] = `
"ToolTip",
"USE_DEBOUNCED_FIELD_DIRTY_KEY",
"useConnectedForm",
"useDatePicker",
"useDebouncedField",
"useField",
"useFormState",
Expand Down
2 changes: 1 addition & 1 deletion packages/gamut/jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ export default base('gamut', {
setupFiles: ['<rootDir>/../../script/jest/base-setup.js'],
setupFilesAfterEnv: ['<rootDir>/../../script/jest/rtl-setup.js'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
transformIgnorePatterns: ['node_modules/(?!(@vidstack/react)/)'],
transformIgnorePatterns: ['node_modules/(?!(@vidstack/react|@formatjs)/)'],
});
1 change: 1 addition & 0 deletions packages/gamut/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"@codecademy/gamut-patterns": "0.10.28",
"@codecademy/gamut-styles": "17.13.1",
"@codecademy/variance": "0.26.1",
"@formatjs/intl-locale": "^5.3.1",
"@react-aria/interactions": "3.25.0",
"@types/marked": "^4.0.8",
"@vidstack/react": "^1.12.12",
Expand Down
174 changes: 174 additions & 0 deletions packages/gamut/src/DatePicker/DatePicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import { MiniArrowRightIcon } from '@codecademy/gamut-icons';
import { useCallback, useId, useMemo, useRef, useState } from 'react';

import { Box, FlexBox } from '../Box';
import { PopoverContainer } from '../PopoverContainer';
import { DatePickerCalendar } from './DatePickerCalendar';
import {
getDefaultRangeQuickActions,
getDefaultSingleQuickActions,
} from './DatePickerCalendar/utils/quickActions';
import { DatePickerProvider } from './DatePickerContext';
import type {
DatePickerContextValue,
DatePickerRangeContextValue,
} from './DatePickerContext/types';
import { DatePickerInput } from './DatePickerInput';
import type { DatePickerProps } from './types';
import { useResolvedLocale } from './utils/locale';
import { DEFAULT_DATE_PICKER_TRANSLATIONS } from './utils/translations';

export const DatePicker: React.FC<DatePickerProps> = (props) => {
const {
locale,
shouldDisableDate,
children,
mode,
translations: translationsProp,
inputSize,
quickActions,
} = props;
const [isCalendarOpen, setIsCalendarOpen] = useState(false);
const [focusGridSignal, setFocusGridSignal] = useState(false);
const [gridFocusRequested, setGridFocusRequested] = useState(false);
const [activeRangePart, setActiveRangePart] =
useState<DatePickerRangeContextValue['activeRangePart']>(null);
const inputRef = useRef<HTMLDivElement | null>(null);
const dialogId = useId();
const calendarDialogId = `datepicker-dialog-${dialogId.replace(/:/g, '')}`;

const clearGridFocusRequest = useCallback(() => {
setGridFocusRequested(false);
}, []);

const resolvedLocale = useResolvedLocale(locale);

const openCalendar = useCallback(() => {
setIsCalendarOpen(true);
}, []);

const focusCalendar = useCallback(() => {
setGridFocusRequested(true);
setFocusGridSignal((signal) => !signal);
}, []);

const closeCalendar = useCallback(() => {
setIsCalendarOpen(false);
setActiveRangePart(null);
setGridFocusRequested(false);
const shell = inputRef.current;
const toFocus =
shell?.querySelector<HTMLElement>('[role="spinbutton"]') ?? shell;
toFocus?.focus();
}, []);

const contextValue = useMemo<DatePickerContextValue>(() => {
const translations = {
...DEFAULT_DATE_PICKER_TRANSLATIONS,
...translationsProp,
};
const resolvedQuickActions =
quickActions ??
(mode === 'range'
? getDefaultRangeQuickActions(translations)
: getDefaultSingleQuickActions(resolvedLocale));
const base = {
isCalendarOpen,
openCalendar,
focusCalendar,
focusGridSignal,
gridFocusRequested,
clearGridFocusRequest,
closeCalendar,
locale: resolvedLocale,
shouldDisableDate,
calendarDialogId,
translations,
quickActions: quickActions === null ? [] : resolvedQuickActions,
};
return mode === 'range'
? {
...base,
mode: 'range',
startDate: props.startDate,
endDate: props.endDate,
activeRangePart,
setActiveRangePart,
onRangeSelection: (startDate: Date | null, endDate: Date | null) => {
props.onStartSelected(startDate);
props.onEndSelected(endDate);
},
}
: {
...base,
mode: 'single',
selectedDate: props.selectedDate,
onSelection: props.onSelected,
};
}, [
translationsProp,
quickActions,
mode,
resolvedLocale,
isCalendarOpen,
openCalendar,
focusCalendar,
focusGridSignal,
gridFocusRequested,
clearGridFocusRequest,
closeCalendar,
shouldDisableDate,
calendarDialogId,
props,
activeRangePart,
]);

const content =
children !== undefined ? (
children
) : (
<>
<FlexBox
gap={inputSize === 'small' ? 4 : 8}
ref={inputRef}
width="fit-content"
>
{mode === 'range' ? (
<>
<DatePickerInput rangePart="start" size={inputSize} />
<Box alignSelf="center" mt={32}>
{/* TODO: Adjust for RTL */}
<MiniArrowRightIcon />
Comment thread
aresnik11 marked this conversation as resolved.
</Box>
<DatePickerInput rangePart="end" size={inputSize} />
</>
) : (
<DatePickerInput size={inputSize} />
)}
</FlexBox>
<PopoverContainer
alignment="bottom-left"
allowPageInteraction
focusOnProps={{ autoFocus: false, focusLock: false }}
invertAxis="x"
isOpen={isCalendarOpen}
targetRef={inputRef}
x={-20}
y={-16}
onRequestClose={closeCalendar}
>
<div
aria-label={contextValue.translations.calendarDialogAriaLabel}
id={calendarDialogId}
role="dialog"
>
<DatePickerCalendar dialogId={calendarDialogId} />
</div>
</PopoverContainer>
</>
);

return (
<DatePickerProvider value={contextValue}>{content}</DatePickerProvider>
);
};
Loading
Loading