@codecademy/styleguide 79.2.1-alpha.9135d1.0 → 79.2.1-alpha.cb2cfc.0
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.
- package/.storybook/components/Elements/DetailedCode/DetailedCodeBody/index.tsx +19 -0
- package/.storybook/components/Elements/DetailedCode/DetailedCodeButton/index.tsx +32 -0
- package/.storybook/components/Elements/DetailedCode/elements.tsx +46 -0
- package/.storybook/components/Elements/DetailedCode/index.tsx +48 -0
- package/.storybook/components/Elements/DetailedCode/types.ts +24 -0
- package/.storybook/components/index.tsx +1 -0
- package/CHANGELOG.md +1 -1
- package/package.json +2 -2
- package/src/lib/Organisms/ConnectedForm/ConnectedForm/ConnectedForm.mdx +3 -70
- package/src/lib/Organisms/ConnectedForm/ConnectedForm/example.ts +67 -0
- package/src/lib/Molecules/DatePicker/Calendar.mdx +0 -26
- package/src/lib/Molecules/DatePicker/Calendar.stories.tsx +0 -53
- package/src/lib/Molecules/DatePicker/DatePicker.stories.tsx +0 -124
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Source } from '@storybook/blocks';
|
|
2
|
+
|
|
3
|
+
import { DetailedCodeBodyWrapper, FloatingIndicator } from '../elements';
|
|
4
|
+
import { DetailedCodeBodyProps } from '../types';
|
|
5
|
+
|
|
6
|
+
export const DetailedCodeBody: React.FC<DetailedCodeBodyProps> = ({
|
|
7
|
+
code,
|
|
8
|
+
language,
|
|
9
|
+
showFloatingBadge = false,
|
|
10
|
+
}) => {
|
|
11
|
+
return (
|
|
12
|
+
<DetailedCodeBodyWrapper hasFloatingBadge={showFloatingBadge}>
|
|
13
|
+
<Source code={code} dark language={language} />
|
|
14
|
+
{showFloatingBadge && (
|
|
15
|
+
<FloatingIndicator aria-label="More code below">...</FloatingIndicator>
|
|
16
|
+
)}
|
|
17
|
+
</DetailedCodeBodyWrapper>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { MiniChevronDownIcon } from '@codecademy/gamut-icons';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { Anchor, Rotation, FlexBox, Text } from '@codecademy/gamut';
|
|
4
|
+
|
|
5
|
+
import { DetailedCodeButtonProps } from '../types';
|
|
6
|
+
|
|
7
|
+
export const DetailedCodeButton: React.FC<DetailedCodeButtonProps> = ({
|
|
8
|
+
isExpanded,
|
|
9
|
+
onToggle,
|
|
10
|
+
language,
|
|
11
|
+
}) => {
|
|
12
|
+
return (
|
|
13
|
+
<Anchor
|
|
14
|
+
aria-expanded={isExpanded}
|
|
15
|
+
px={16}
|
|
16
|
+
py={12}
|
|
17
|
+
variant="interface"
|
|
18
|
+
width="100%"
|
|
19
|
+
onClick={onToggle}
|
|
20
|
+
>
|
|
21
|
+
<FlexBox columnGap={16} justifyContent="space-between">
|
|
22
|
+
<Text>{language}</Text>
|
|
23
|
+
<FlexBox columnGap={8} flexDirection="row" alignItems="center">
|
|
24
|
+
<Text>{isExpanded ? 'Show Less Code' : 'Show More Code'}</Text>
|
|
25
|
+
<Rotation rotated={isExpanded}>
|
|
26
|
+
<MiniChevronDownIcon aria-hidden size={16} />
|
|
27
|
+
</Rotation>
|
|
28
|
+
</FlexBox>
|
|
29
|
+
</FlexBox>
|
|
30
|
+
</Anchor>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { css } from '@codecademy/gamut-styles';
|
|
2
|
+
import styled from '@emotion/styled';
|
|
3
|
+
import { FlexBox } from '@codecademy/gamut';
|
|
4
|
+
|
|
5
|
+
export const DetailedCodeWrapper = styled(FlexBox)(
|
|
6
|
+
css({
|
|
7
|
+
width: '100%',
|
|
8
|
+
flexDirection: 'column',
|
|
9
|
+
borderRadius: 'md',
|
|
10
|
+
border: 1,
|
|
11
|
+
bg: 'background',
|
|
12
|
+
})
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
export const DetailedCodeBodyWrapper = styled(FlexBox)<{
|
|
16
|
+
hasFloatingBadge?: boolean;
|
|
17
|
+
}>(({ hasFloatingBadge }) =>
|
|
18
|
+
css({
|
|
19
|
+
position: 'relative',
|
|
20
|
+
flexDirection: 'column',
|
|
21
|
+
/* Override Storybook's Source component default styles to remove unwanted spacing and borders in the container */
|
|
22
|
+
'& .docblock-source': {
|
|
23
|
+
borderRadius: 'none',
|
|
24
|
+
margin: 0,
|
|
25
|
+
pb: hasFloatingBadge ? 48 : 0,
|
|
26
|
+
},
|
|
27
|
+
})
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
export const FloatingIndicator = styled(FlexBox)(
|
|
31
|
+
css({
|
|
32
|
+
position: 'absolute',
|
|
33
|
+
bottom: 16,
|
|
34
|
+
left: '50%',
|
|
35
|
+
transform: 'translateX(-50%)',
|
|
36
|
+
zIndex: 1,
|
|
37
|
+
bg: 'inherit',
|
|
38
|
+
px: 12,
|
|
39
|
+
py: 4,
|
|
40
|
+
borderRadius: 'lg',
|
|
41
|
+
fontSize: 26,
|
|
42
|
+
fontWeight: 700,
|
|
43
|
+
color: 'white',
|
|
44
|
+
letterSpacing: '0.1em',
|
|
45
|
+
})
|
|
46
|
+
);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { DetailedCodeBody } from './DetailedCodeBody';
|
|
4
|
+
import { DetailedCodeButton } from './DetailedCodeButton';
|
|
5
|
+
import { DetailedCodeWrapper } from './elements';
|
|
6
|
+
import { DetailedCodeProps } from './types';
|
|
7
|
+
|
|
8
|
+
const DEFAULT_PREVIEW_LINES = 20;
|
|
9
|
+
const DEFAULT_LANGUAGE = 'tsx';
|
|
10
|
+
|
|
11
|
+
export const DetailedCode: React.FC<DetailedCodeProps> = ({
|
|
12
|
+
code,
|
|
13
|
+
initiallyExpanded = false,
|
|
14
|
+
language = DEFAULT_LANGUAGE,
|
|
15
|
+
preview = false,
|
|
16
|
+
previewLines = DEFAULT_PREVIEW_LINES,
|
|
17
|
+
}) => {
|
|
18
|
+
const [isExpanded, setIsExpanded] = useState(initiallyExpanded);
|
|
19
|
+
const normalizedPreviewLines = Math.max(0, previewLines);
|
|
20
|
+
const previewEnabled = preview && normalizedPreviewLines > 0;
|
|
21
|
+
|
|
22
|
+
const codeLines = code.split('\n');
|
|
23
|
+
const hasMoreCode =
|
|
24
|
+
previewEnabled && codeLines.length > normalizedPreviewLines;
|
|
25
|
+
|
|
26
|
+
const previewCode = previewEnabled
|
|
27
|
+
? codeLines.slice(0, normalizedPreviewLines).join('\n')
|
|
28
|
+
: code;
|
|
29
|
+
|
|
30
|
+
const displayedCode = isExpanded ? code : previewCode;
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<DetailedCodeWrapper>
|
|
34
|
+
<DetailedCodeBody
|
|
35
|
+
code={displayedCode}
|
|
36
|
+
language={language}
|
|
37
|
+
showFloatingBadge={hasMoreCode && !isExpanded}
|
|
38
|
+
/>
|
|
39
|
+
{hasMoreCode && (
|
|
40
|
+
<DetailedCodeButton
|
|
41
|
+
isExpanded={isExpanded}
|
|
42
|
+
onToggle={() => setIsExpanded((prev) => !prev)}
|
|
43
|
+
language={language}
|
|
44
|
+
/>
|
|
45
|
+
)}
|
|
46
|
+
</DetailedCodeWrapper>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Source } from '@storybook/blocks';
|
|
2
|
+
import { ComponentProps } from 'react';
|
|
3
|
+
|
|
4
|
+
type SourceLanguage = ComponentProps<typeof Source>['language'];
|
|
5
|
+
|
|
6
|
+
export interface DetailedCodeProps {
|
|
7
|
+
code: string;
|
|
8
|
+
language?: SourceLanguage;
|
|
9
|
+
initiallyExpanded?: boolean;
|
|
10
|
+
preview?: boolean;
|
|
11
|
+
previewLines?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface DetailedCodeButtonProps {
|
|
15
|
+
isExpanded: boolean;
|
|
16
|
+
onToggle: () => void;
|
|
17
|
+
language: SourceLanguage;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface DetailedCodeBodyProps {
|
|
21
|
+
code: string;
|
|
22
|
+
language: SourceLanguage;
|
|
23
|
+
showFloatingBadge?: boolean;
|
|
24
|
+
}
|
|
@@ -2,6 +2,7 @@ export * from './ImageWrapper';
|
|
|
2
2
|
export * from './TokenTable';
|
|
3
3
|
export * from './Headers';
|
|
4
4
|
export * from './Elements/Callout';
|
|
5
|
+
export * from './Elements/DetailedCode';
|
|
5
6
|
export * from './Elements/KeyboardKey';
|
|
6
7
|
export * from './Elements/Markdown';
|
|
7
8
|
export * from './Elements/ImageGallery';
|
package/CHANGELOG.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
-
### [79.2.1-alpha.
|
|
6
|
+
### [79.2.1-alpha.cb2cfc.0](https://github.com/Codecademy/gamut/compare/@codecademy/styleguide@79.2.0...@codecademy/styleguide@79.2.1-alpha.cb2cfc.0) (2026-03-16)
|
|
7
7
|
|
|
8
8
|
**Note:** Version bump only for package @codecademy/styleguide
|
|
9
9
|
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codecademy/styleguide",
|
|
3
3
|
"description": "Styleguide & Component library for codecademy.com",
|
|
4
|
-
"version": "79.2.1-alpha.
|
|
4
|
+
"version": "79.2.1-alpha.cb2cfc.0",
|
|
5
5
|
"author": "Codecademy Engineering",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
10
10
|
"repository": "git@github.com:Codecademy/gamut.git",
|
|
11
|
-
"gitHead": "
|
|
11
|
+
"gitHead": "8f7b19e053cc39e26d9be621c1b408d692e012f5"
|
|
12
12
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Canvas, Controls, Meta } from '@storybook/blocks';
|
|
2
2
|
|
|
3
|
-
import { ComponentHeader, LinkTo } from '~styleguide/blocks';
|
|
3
|
+
import { ComponentHeader, DetailedCode, LinkTo } from '~styleguide/blocks';
|
|
4
4
|
|
|
5
5
|
import * as ConnectedFormStories from './ConnectedForm.stories';
|
|
6
|
+
import { example } from './example';
|
|
6
7
|
|
|
7
8
|
export const parameters = {
|
|
8
9
|
title: 'ConnectedForm',
|
|
@@ -40,75 +41,7 @@ This hook also returns the `FormRequiredText` component - include this before yo
|
|
|
40
41
|
|
|
41
42
|
### Example code
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
import {
|
|
45
|
-
ConnectedCheckbox,
|
|
46
|
-
ConnectedInput,
|
|
47
|
-
ConnectedSelect,
|
|
48
|
-
useConnectedForm,
|
|
49
|
-
} from '@codecademy/gamut';
|
|
50
|
-
|
|
51
|
-
import { TerminalIcon } from '@codecademy/gamut-icons';
|
|
52
|
-
|
|
53
|
-
export const GoodForm = () => {
|
|
54
|
-
const {
|
|
55
|
-
ConnectedFormGroup,
|
|
56
|
-
ConnectedForm,
|
|
57
|
-
connectedFormProps,
|
|
58
|
-
FormRequiredText,
|
|
59
|
-
} = useConnectedForm({
|
|
60
|
-
defaultValues: {
|
|
61
|
-
thisField: true,
|
|
62
|
-
thatField: 'zero',
|
|
63
|
-
anotherField: 'state your name.',
|
|
64
|
-
},
|
|
65
|
-
validationRules: {
|
|
66
|
-
thisField: { required: 'you need to check this.' },
|
|
67
|
-
thatField: {
|
|
68
|
-
pattern: {
|
|
69
|
-
value: /^(?:(?!zero).)*$/,
|
|
70
|
-
message: 'literally anything but zero',
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
return (
|
|
77
|
-
<ConnectedForm
|
|
78
|
-
onSubmit={({ thisField }) => console.log(thisField)}
|
|
79
|
-
resetOnSubmit
|
|
80
|
-
{...connectedFormProps}
|
|
81
|
-
>
|
|
82
|
-
<SubmitButton>submit this form.</SubmitButton>
|
|
83
|
-
<ConnectedFormGroup
|
|
84
|
-
name="thisField"
|
|
85
|
-
label="cool checkbox bruh"
|
|
86
|
-
field={{
|
|
87
|
-
component: ConnectedCheckbox,
|
|
88
|
-
label: 'check it ouuut',
|
|
89
|
-
}}
|
|
90
|
-
/>
|
|
91
|
-
<ConnectedFormGroup
|
|
92
|
-
name="thatField"
|
|
93
|
-
label="cool select dude"
|
|
94
|
-
field={{
|
|
95
|
-
component: ConnectedSelect,
|
|
96
|
-
options: ['one', 'two', 'zero'],
|
|
97
|
-
}}
|
|
98
|
-
/>
|
|
99
|
-
<ConnectedFormGroup
|
|
100
|
-
name="anotherField"
|
|
101
|
-
label="cool input"
|
|
102
|
-
field={{
|
|
103
|
-
component: ConnectedInput,
|
|
104
|
-
icon: TerminalIcon,
|
|
105
|
-
}}
|
|
106
|
-
/>
|
|
107
|
-
<FormRequiredText />
|
|
108
|
-
</ConnectedForm>
|
|
109
|
-
);
|
|
110
|
-
};
|
|
111
|
-
```
|
|
44
|
+
<DetailedCode code={example} language="tsx" preview />
|
|
112
45
|
|
|
113
46
|
## Variants
|
|
114
47
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export const example = `import {
|
|
2
|
+
ConnectedCheckbox,
|
|
3
|
+
ConnectedInput,
|
|
4
|
+
ConnectedSelect,
|
|
5
|
+
useConnectedForm,
|
|
6
|
+
} from '@codecademy/gamut';
|
|
7
|
+
|
|
8
|
+
import { TerminalIcon } from '@codecademy/gamut-icons';
|
|
9
|
+
|
|
10
|
+
export const GoodForm = () => {
|
|
11
|
+
const {
|
|
12
|
+
ConnectedFormGroup,
|
|
13
|
+
ConnectedForm,
|
|
14
|
+
connectedFormProps,
|
|
15
|
+
FormRequiredText,
|
|
16
|
+
} = useConnectedForm({
|
|
17
|
+
defaultValues: {
|
|
18
|
+
thisField: true,
|
|
19
|
+
thatField: 'zero',
|
|
20
|
+
anotherField: 'state your name.',
|
|
21
|
+
},
|
|
22
|
+
validationRules: {
|
|
23
|
+
thisField: { required: 'you need to check this.' },
|
|
24
|
+
thatField: {
|
|
25
|
+
pattern: {
|
|
26
|
+
value: /^(?:(?!zero).)*$/,
|
|
27
|
+
message: 'literally anything but zero',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<ConnectedForm
|
|
35
|
+
onSubmit={({ thisField }) => console.log(thisField)}
|
|
36
|
+
resetOnSubmit
|
|
37
|
+
{...connectedFormProps}
|
|
38
|
+
>
|
|
39
|
+
<SubmitButton>submit this form.</SubmitButton>
|
|
40
|
+
<ConnectedFormGroup
|
|
41
|
+
name="thisField"
|
|
42
|
+
label="cool checkbox bruh"
|
|
43
|
+
field={{
|
|
44
|
+
component: ConnectedCheckbox,
|
|
45
|
+
label: 'check it ouuut',
|
|
46
|
+
}}
|
|
47
|
+
/>
|
|
48
|
+
<ConnectedFormGroup
|
|
49
|
+
name="thatField"
|
|
50
|
+
label="cool select dude"
|
|
51
|
+
field={{
|
|
52
|
+
component: ConnectedSelect,
|
|
53
|
+
options: ['one', 'two', 'zero'],
|
|
54
|
+
}}
|
|
55
|
+
/>
|
|
56
|
+
<ConnectedFormGroup
|
|
57
|
+
name="anotherField"
|
|
58
|
+
label="cool input"
|
|
59
|
+
field={{
|
|
60
|
+
component: ConnectedInput,
|
|
61
|
+
icon: TerminalIcon,
|
|
62
|
+
}}
|
|
63
|
+
/>
|
|
64
|
+
<FormRequiredText />
|
|
65
|
+
</ConnectedForm>
|
|
66
|
+
);
|
|
67
|
+
};`;
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { Canvas, Controls, Meta } from '@storybook/blocks';
|
|
2
|
-
|
|
3
|
-
import { ComponentHeader } from '~styleguide/blocks';
|
|
4
|
-
|
|
5
|
-
import * as CalendarStories from './Calendar.stories';
|
|
6
|
-
|
|
7
|
-
export const parameters = {
|
|
8
|
-
title: 'DatePicker/Calendar',
|
|
9
|
-
subtitle: `Calendar grid with header (month/year + prev/next), body (day grid), and footer (Clear, Today, quick actions). Used inside DatePickerCalendar.`,
|
|
10
|
-
status: 'current',
|
|
11
|
-
source: {
|
|
12
|
-
repo: 'gamut',
|
|
13
|
-
githubLink:
|
|
14
|
-
'https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/DatePicker/Calendar',
|
|
15
|
-
},
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
<Meta of={CalendarStories} />
|
|
19
|
-
|
|
20
|
-
<ComponentHeader {...parameters} />
|
|
21
|
-
|
|
22
|
-
## Playground
|
|
23
|
-
|
|
24
|
-
<Canvas sourceState="shown" of={CalendarStories.Default} />
|
|
25
|
-
|
|
26
|
-
<Controls />
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Calendar,
|
|
3
|
-
CalendarBody,
|
|
4
|
-
CalendarFooter,
|
|
5
|
-
CalendarHeader,
|
|
6
|
-
} from '@codecademy/gamut';
|
|
7
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
8
|
-
import { useId, useState } from 'react';
|
|
9
|
-
|
|
10
|
-
const meta: Meta<typeof Calendar> = {
|
|
11
|
-
component: Calendar,
|
|
12
|
-
title: 'Molecules/DatePicker/Calendar',
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export default meta;
|
|
16
|
-
|
|
17
|
-
type Story = StoryObj<typeof Calendar>;
|
|
18
|
-
|
|
19
|
-
export const Default: Story = {
|
|
20
|
-
render: function CalendarStory() {
|
|
21
|
-
const headingId = useId();
|
|
22
|
-
const [visibleDate, setVisibleDate] = useState(() => new Date());
|
|
23
|
-
const [selectedDate, setSelectedDate] = useState<Date | null>(null);
|
|
24
|
-
const [focusedDate, setFocusedDate] = useState<Date | null>(
|
|
25
|
-
() => new Date()
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<Calendar>
|
|
30
|
-
<CalendarHeader
|
|
31
|
-
currentMonthYear={visibleDate}
|
|
32
|
-
headingId={headingId}
|
|
33
|
-
locale="en-US"
|
|
34
|
-
onCurrentMonthYearChange={setVisibleDate}
|
|
35
|
-
/>
|
|
36
|
-
<CalendarBody
|
|
37
|
-
focusedDate={focusedDate}
|
|
38
|
-
labelledById={headingId}
|
|
39
|
-
locale="en-US"
|
|
40
|
-
selectedDate={selectedDate}
|
|
41
|
-
visibleDate={visibleDate}
|
|
42
|
-
onDateSelect={setSelectedDate}
|
|
43
|
-
onFocusedDateChange={setFocusedDate}
|
|
44
|
-
onVisibleDateChange={setVisibleDate}
|
|
45
|
-
/>
|
|
46
|
-
<CalendarFooter
|
|
47
|
-
onCurrentMonthYearChange={setVisibleDate}
|
|
48
|
-
onSelectedDateChange={setSelectedDate}
|
|
49
|
-
/>
|
|
50
|
-
</Calendar>
|
|
51
|
-
);
|
|
52
|
-
},
|
|
53
|
-
};
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Box,
|
|
3
|
-
DatePicker,
|
|
4
|
-
DatePickerCalendar,
|
|
5
|
-
DatePickerInput,
|
|
6
|
-
PopoverContainer,
|
|
7
|
-
useDatePicker,
|
|
8
|
-
} from '@codecademy/gamut';
|
|
9
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
10
|
-
import { useRef, useState } from 'react';
|
|
11
|
-
|
|
12
|
-
const meta: Meta<typeof DatePicker> = {
|
|
13
|
-
component: DatePicker,
|
|
14
|
-
title: 'Molecules/DatePicker/DatePicker',
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export default meta;
|
|
18
|
-
|
|
19
|
-
type Story = StoryObj<typeof DatePicker>;
|
|
20
|
-
|
|
21
|
-
export const Default: Story = {
|
|
22
|
-
render: function DatePickerStory() {
|
|
23
|
-
const [selectedDate, setSelectedDate] = useState<Date | null>(null);
|
|
24
|
-
return (
|
|
25
|
-
<Box p={32}>
|
|
26
|
-
<DatePicker
|
|
27
|
-
label="Date"
|
|
28
|
-
locale="de-DE"
|
|
29
|
-
placeholder="MM/DD/YYYY"
|
|
30
|
-
selectedDate={selectedDate}
|
|
31
|
-
setSelectedDate={setSelectedDate}
|
|
32
|
-
/>
|
|
33
|
-
</Box>
|
|
34
|
-
);
|
|
35
|
-
},
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
export const WithInitialDate: Story = {
|
|
39
|
-
render: function DatePickerStory() {
|
|
40
|
-
const [selectedDate, setSelectedDate] = useState<Date | null>(
|
|
41
|
-
() => new Date(2026, 1, 15)
|
|
42
|
-
);
|
|
43
|
-
return (
|
|
44
|
-
<DatePicker
|
|
45
|
-
label="Date"
|
|
46
|
-
placeholder="MM/DD/YYYY"
|
|
47
|
-
selectedDate={selectedDate}
|
|
48
|
-
setSelectedDate={setSelectedDate}
|
|
49
|
-
/>
|
|
50
|
-
);
|
|
51
|
-
},
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
/** Range mode: two inputs for start and end date. First calendar click sets start, second sets end. */
|
|
55
|
-
export const Range: Story = {
|
|
56
|
-
render: function DatePickerStory() {
|
|
57
|
-
const [startDate, setStartDate] = useState<Date | null>(null);
|
|
58
|
-
const [endDate, setEndDate] = useState<Date | null>(null);
|
|
59
|
-
return (
|
|
60
|
-
<Box p={32}>
|
|
61
|
-
<DatePicker
|
|
62
|
-
endDate={endDate}
|
|
63
|
-
endLabel="End date"
|
|
64
|
-
mode="range"
|
|
65
|
-
setEndDate={setEndDate}
|
|
66
|
-
setStartDate={setStartDate}
|
|
67
|
-
startDate={startDate}
|
|
68
|
-
startLabel="Start date"
|
|
69
|
-
/>
|
|
70
|
-
</Box>
|
|
71
|
-
);
|
|
72
|
-
},
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Composed usage: DatePicker with children provides shared state via context.
|
|
77
|
-
* The child uses useDatePicker() to get open/close and inputRef, then composes
|
|
78
|
-
* DatePickerInput and DatePickerCalendar with a custom PopoverContainer layout.
|
|
79
|
-
*/
|
|
80
|
-
export const ComposedWithContext: Story = {
|
|
81
|
-
render: function DatePickerStory() {
|
|
82
|
-
const [selectedDate, setSelectedDate] = useState<Date | null>(null);
|
|
83
|
-
return (
|
|
84
|
-
<Box p={32}>
|
|
85
|
-
<DatePicker
|
|
86
|
-
label="Start date"
|
|
87
|
-
placeholder="MM/DD/YYYY"
|
|
88
|
-
selectedDate={selectedDate}
|
|
89
|
-
setSelectedDate={setSelectedDate}
|
|
90
|
-
>
|
|
91
|
-
<ComposedDatePickerLayout />
|
|
92
|
-
</DatePicker>
|
|
93
|
-
</Box>
|
|
94
|
-
);
|
|
95
|
-
},
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
function ComposedDatePickerLayout() {
|
|
99
|
-
const { isCalendarOpen, openCalendar, closeCalendar, calendarDialogId } =
|
|
100
|
-
useDatePicker();
|
|
101
|
-
const inputRef = useRef<HTMLInputElement | null>(null);
|
|
102
|
-
|
|
103
|
-
return (
|
|
104
|
-
<>
|
|
105
|
-
<Box width="fit-content" onClick={openCalendar}>
|
|
106
|
-
<DatePickerInput ref={inputRef} />
|
|
107
|
-
</Box>
|
|
108
|
-
<PopoverContainer
|
|
109
|
-
alignment="bottom-left"
|
|
110
|
-
allowPageInteraction
|
|
111
|
-
focusOnProps={{ autoFocus: false, focusLock: false }}
|
|
112
|
-
invertAxis="x"
|
|
113
|
-
isOpen={isCalendarOpen}
|
|
114
|
-
offset={10}
|
|
115
|
-
targetRef={inputRef}
|
|
116
|
-
onRequestClose={closeCalendar}
|
|
117
|
-
>
|
|
118
|
-
<div aria-label="Choose date" id={calendarDialogId} role="dialog">
|
|
119
|
-
<DatePickerCalendar dialogId={calendarDialogId} />
|
|
120
|
-
</div>
|
|
121
|
-
</PopoverContainer>
|
|
122
|
-
</>
|
|
123
|
-
);
|
|
124
|
-
}
|