@byline/ui 6.8.0 → 6.10.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.
Files changed (51) hide show
  1. package/README.md +43 -5
  2. package/package.json +16 -7
  3. package/src/components/accordion/accordion.stories.tsx +121 -0
  4. package/src/components/animation/fade-in-lift.stories.tsx +80 -0
  5. package/src/components/avatar/avatar.stories.tsx +39 -0
  6. package/src/components/badge/badge.stories.tsx +41 -0
  7. package/src/components/button/button-group.stories.tsx +44 -0
  8. package/src/components/button/button-intents.stories.tsx +97 -0
  9. package/src/components/button/button-overrides.stories.tsx +59 -0
  10. package/src/components/button/button-test.stories.tsx +59 -0
  11. package/src/components/button/button-variants.stories.tsx +57 -0
  12. package/src/components/button/combo-buttons.stories.tsx +82 -0
  13. package/src/components/button/control-buttons.stories.tsx +60 -0
  14. package/src/components/button/copy-button.stories.tsx +50 -0
  15. package/src/components/button/icon-button.stories.tsx +140 -0
  16. package/src/components/card/card.stories.tsx +60 -0
  17. package/src/components/chips/chip.stories.tsx +120 -0
  18. package/src/components/container/container.stories.tsx +35 -0
  19. package/src/components/dropdown/dropdown.stories.tsx +108 -0
  20. package/src/components/hamburger/hamburger.stories.tsx +33 -0
  21. package/src/components/inputs/autocomplete.stories.tsx +189 -0
  22. package/src/components/inputs/calendar.stories.tsx +141 -0
  23. package/src/components/inputs/checkbox-group.stories.tsx +43 -0
  24. package/src/components/inputs/checkbox.stories.tsx +284 -0
  25. package/src/components/inputs/input.stories.tsx +189 -0
  26. package/src/components/inputs/radio-group.stories.tsx +73 -0
  27. package/src/components/inputs/select.stories.tsx +70 -0
  28. package/src/components/inputs/text-area.stories.tsx +45 -0
  29. package/src/components/notifications/alert.stories.tsx +75 -0
  30. package/src/components/notifications/toast.stories.tsx +40 -0
  31. package/src/components/pager/pagination.stories.tsx +189 -0
  32. package/src/components/scroll-area/scroll-area.stories.tsx +33 -0
  33. package/src/components/shimmer/shimmer.stories.tsx +24 -0
  34. package/src/components/table/table.stories.tsx +94 -0
  35. package/src/components/tabs/tabs.stories.tsx +47 -0
  36. package/src/components/tooltip/tooltip.stories.tsx +56 -0
  37. package/src/icons/icons.stories.tsx +67 -0
  38. package/src/icons/index.stories.tsx +24 -0
  39. package/src/loaders/loaders.stories.tsx +188 -0
  40. package/src/styles/base/colors.stories.tsx +671 -0
  41. package/src/styles/theme/theme.stories.tsx +40 -0
  42. package/src/styles/typography/article.stories.tsx +39 -0
  43. package/src/styles/typography/code.stories.tsx +73 -0
  44. package/src/styles/typography/default.stories.tsx +49 -0
  45. package/src/styles/typography/lists.stories.tsx +48 -0
  46. package/src/styles/typography/quote.stories.tsx +38 -0
  47. package/src/widgets/datepicker/datepicker.stories.tsx +42 -0
  48. package/src/widgets/drawer/drawer.stories.tsx +281 -0
  49. package/src/widgets/modal/modal.stories.tsx +63 -0
  50. package/src/widgets/search/search.stories.tsx +18 -0
  51. package/src/widgets/timeline/timeline.stories.tsx +50 -0
@@ -0,0 +1,108 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+
3
+ import { SignOutIcon } from '../../icons/sign-out-icon.js'
4
+ import { UserIcon } from '../../icons/user-icon.js'
5
+ import { Button } from '../button/button.js'
6
+ import { Dropdown as DropdownComponent } from './dropdown.js'
7
+
8
+ const meta: Meta = {
9
+ /* 👇 The title prop is optional.
10
+ * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
11
+ * to learn how to generate automatic titles
12
+ */
13
+ title: 'Components/Dropdown',
14
+ component: DropdownComponent.Root,
15
+ }
16
+
17
+ export default meta
18
+
19
+ type Story = StoryObj<typeof DropdownComponent.Root>
20
+
21
+ export const Dropdown: Story = {
22
+ args: {
23
+ modal: false,
24
+ },
25
+ render: (args) => {
26
+ return (
27
+ <>
28
+ <style>
29
+ {`
30
+ .dropdown-item-content {
31
+ display: flex;
32
+ align-items: center;
33
+ }
34
+
35
+ .dropdown-item-content-icon {
36
+ display: inline-block;
37
+ width: 28px;
38
+ }
39
+
40
+ .dropdown-item-content-text {
41
+ text-align: left;
42
+ display: inline-block;
43
+ width: 100%;
44
+ flex: 1;
45
+ }
46
+
47
+ `}
48
+ </style>
49
+ <div
50
+ style={{
51
+ maxWidth: '400px',
52
+ margin: '2rem 0 24px',
53
+ display: 'flex',
54
+ justifyContent: 'center',
55
+ }}
56
+ >
57
+ <DropdownComponent.Root {...args}>
58
+ <DropdownComponent.Trigger render={<Button size="sm" variant="filled" />}>
59
+ +
60
+ </DropdownComponent.Trigger>
61
+
62
+ <DropdownComponent.Portal>
63
+ <DropdownComponent.Content align="end" data-side="top" sideOffset={10}>
64
+ <DropdownComponent.Item>
65
+ <div className="dropdown-item-content">
66
+ <span className="dropdown-item-content-icon">
67
+ <UserIcon width="22px" height="22px" />
68
+ </span>
69
+ <span className="dropdown-item-content-text">Account</span>
70
+ </div>
71
+ </DropdownComponent.Item>
72
+ <DropdownComponent.Item>
73
+ <div className="dropdown-item-content">
74
+ <span className="dropdown-item-content-icon" />
75
+ <span className="dropdown-item-content-text">Menu Item 2</span>
76
+ </div>
77
+ </DropdownComponent.Item>
78
+ <DropdownComponent.Separator />
79
+ <DropdownComponent.Item>
80
+ <div className="dropdown-item-content">
81
+ <span className="dropdown-item-content-icon">
82
+ <SignOutIcon />
83
+ </span>
84
+ <span className="dropdown-item-content-text">
85
+ <button
86
+ type="button"
87
+ style={{
88
+ textAlign: 'left',
89
+ display: 'inline-block',
90
+ width: '100%',
91
+ flex: 1,
92
+ lineHeight: 1,
93
+ color: 'var(--text)',
94
+ }}
95
+ >
96
+ Sign Out
97
+ </button>
98
+ </span>
99
+ </div>
100
+ </DropdownComponent.Item>
101
+ </DropdownComponent.Content>
102
+ </DropdownComponent.Portal>
103
+ </DropdownComponent.Root>
104
+ </div>
105
+ </>
106
+ )
107
+ },
108
+ }
@@ -0,0 +1,33 @@
1
+ import type React from 'react'
2
+ import { useState } from 'react'
3
+
4
+ import type { Meta } from '@storybook/react-vite'
5
+
6
+ import { Section } from '../section/section.js'
7
+ import { Hamburger as HamburgerComponent } from './hamburger.js'
8
+
9
+ const meta: Meta<typeof HamburgerComponent> = {
10
+ /* 👇 The title prop is optional.
11
+ * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
12
+ * to learn how to generate automatic titles
13
+ */
14
+ title: 'Components/Hamburger',
15
+ component: HamburgerComponent,
16
+ }
17
+
18
+ export default meta
19
+
20
+ export const Hamburger = (): React.JSX.Element => {
21
+ const [open, setOpen] = useState(false)
22
+
23
+ return (
24
+ <Section style={{ height: '100vh', margin: '2rem auto' }}>
25
+ <HamburgerComponent
26
+ open={open}
27
+ onChange={(open) => {
28
+ setOpen(open)
29
+ }}
30
+ />
31
+ </Section>
32
+ )
33
+ }
@@ -0,0 +1,189 @@
1
+ import type React from 'react'
2
+
3
+ import { capitalize } from '../../utils/capitalize.js'
4
+ import { intent } from '../@types/shared.js'
5
+ import { size, variant } from './@types/autocomplete.js'
6
+ import { Autocomplete, AutocompleteItem } from './autocomplete.js'
7
+
8
+ export default {
9
+ title: 'Components/Inputs/Autocomplete',
10
+ component: Autocomplete,
11
+ argTypes: {},
12
+ }
13
+
14
+ interface Tag {
15
+ id: string
16
+ value: string
17
+ }
18
+
19
+ const tags: Tag[] = [
20
+ { id: 't1', value: 'feature' },
21
+ { id: 't2', value: 'fix' },
22
+ { id: 't3', value: 'bug' },
23
+ { id: 't4', value: 'docs' },
24
+ { id: 't5', value: 'internal' },
25
+ { id: 't6', value: 'mobile' },
26
+ { id: 'c-accordion', value: 'component: accordion' },
27
+ { id: 'c-alert-dialog', value: 'component: alert dialog' },
28
+ { id: 'c-autocomplete', value: 'component: autocomplete' },
29
+ { id: 'c-avatar', value: 'component: avatar' },
30
+ { id: 'c-checkbox', value: 'component: checkbox' },
31
+ { id: 'c-dialog', value: 'component: dialog' },
32
+ { id: 'c-input', value: 'component: input' },
33
+ { id: 'c-select', value: 'component: select' },
34
+ { id: 'c-tabs', value: 'component: tabs' },
35
+ { id: 'c-toast', value: 'component: toast' },
36
+ { id: 'c-tooltip', value: 'component: tooltip' },
37
+ ]
38
+
39
+ export const Default = (): React.JSX.Element => {
40
+ return (
41
+ <div style={{ maxWidth: '600px', margin: '2rem auto' }}>
42
+ <Autocomplete<Tag>
43
+ id="tags-default"
44
+ label="Search tags"
45
+ placeholder="e.g. feature"
46
+ helpText="Start typing to filter tags."
47
+ items={tags}
48
+ >
49
+ {(tag: Tag) => (
50
+ <AutocompleteItem key={tag.id} value={tag}>
51
+ {tag.value}
52
+ </AutocompleteItem>
53
+ )}
54
+ </Autocomplete>
55
+ </div>
56
+ )
57
+ }
58
+
59
+ export const Variants = (): React.JSX.Element => {
60
+ return (
61
+ <div style={{ maxWidth: '1100px', margin: '0 auto' }}>
62
+ {variant.map((v) => {
63
+ return (
64
+ <div
65
+ key={v}
66
+ style={{
67
+ marginBottom: '2rem',
68
+ display: 'grid',
69
+ gridTemplateColumns: 'repeat(4, minmax(0, 1fr))',
70
+ alignItems: 'center',
71
+ gap: '3rem',
72
+ }}
73
+ >
74
+ {size.map((s) => {
75
+ return (
76
+ <Autocomplete<Tag>
77
+ key={`${v}-${s}`}
78
+ id={`${v}-${s}`}
79
+ variant={v}
80
+ inputSize={s}
81
+ label={`${capitalize(v)} ${s.toUpperCase()}`}
82
+ placeholder={`${capitalize(v)} ${s.toUpperCase()}`}
83
+ helpText={`${capitalize(v)} variant, ${s.toUpperCase()} size.`}
84
+ items={tags}
85
+ >
86
+ {(tag: Tag) => (
87
+ <AutocompleteItem key={tag.id} value={tag}>
88
+ {tag.value}
89
+ </AutocompleteItem>
90
+ )}
91
+ </Autocomplete>
92
+ )
93
+ })}
94
+ <Autocomplete<Tag>
95
+ key={`${v}-disabled`}
96
+ id={`${v}-disabled`}
97
+ variant={v}
98
+ disabled
99
+ label={`${capitalize(v)} Disabled`}
100
+ placeholder={`${capitalize(v)} disabled`}
101
+ helpText={`${capitalize(v)} variant, disabled.`}
102
+ items={tags}
103
+ >
104
+ {(tag: Tag) => (
105
+ <AutocompleteItem key={tag.id} value={tag}>
106
+ {tag.value}
107
+ </AutocompleteItem>
108
+ )}
109
+ </Autocomplete>
110
+ </div>
111
+ )
112
+ })}
113
+ </div>
114
+ )
115
+ }
116
+
117
+ export const Intents = (): React.JSX.Element => {
118
+ return (
119
+ <div style={{ maxWidth: '800px', margin: '0 auto' }}>
120
+ {intent.map((i) => {
121
+ return (
122
+ <div key={i} style={{ marginBottom: '12px' }}>
123
+ <Autocomplete<Tag>
124
+ id={`intent-${i}`}
125
+ variant="outlined"
126
+ intent={i}
127
+ inputSize="md"
128
+ label={capitalize(i)}
129
+ placeholder={capitalize(i)}
130
+ helpText={`${capitalize(i)} intent autocomplete.`}
131
+ items={tags}
132
+ >
133
+ {(tag: Tag) => (
134
+ <AutocompleteItem key={tag.id} value={tag}>
135
+ {tag.value}
136
+ </AutocompleteItem>
137
+ )}
138
+ </Autocomplete>
139
+ </div>
140
+ )
141
+ })}
142
+ </div>
143
+ )
144
+ }
145
+
146
+ // biome-ignore lint/suspicious/noShadowRestrictedNames: storybook convention
147
+ export const Error = (): React.JSX.Element => {
148
+ return (
149
+ <div style={{ maxWidth: '600px', margin: '0 auto' }}>
150
+ <Autocomplete<Tag>
151
+ id="tags-error"
152
+ required
153
+ label="Search tags"
154
+ placeholder="e.g. feature"
155
+ helpText="Please select a tag."
156
+ error={true}
157
+ errorText="Please select a valid tag."
158
+ items={tags}
159
+ >
160
+ {(tag: Tag) => (
161
+ <AutocompleteItem key={tag.id} value={tag}>
162
+ {tag.value}
163
+ </AutocompleteItem>
164
+ )}
165
+ </Autocomplete>
166
+ </div>
167
+ )
168
+ }
169
+
170
+ export const WithoutCustomItem = (): React.JSX.Element => {
171
+ const fruits = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
172
+ return (
173
+ <div style={{ maxWidth: '600px', margin: '2rem auto' }}>
174
+ <Autocomplete<string>
175
+ id="fruits"
176
+ label="Search fruits"
177
+ placeholder="e.g. Apple"
178
+ helpText="Using plain strings without AutocompleteItem wrapper."
179
+ items={fruits}
180
+ >
181
+ {(fruit: string) => (
182
+ <AutocompleteItem key={fruit} value={fruit}>
183
+ {fruit}
184
+ </AutocompleteItem>
185
+ )}
186
+ </Autocomplete>
187
+ </div>
188
+ )
189
+ }
@@ -0,0 +1,141 @@
1
+ import { useRef, useState } from 'react'
2
+
3
+ import type { Meta } from '@storybook/react-vite'
4
+ import { format } from 'date-fns'
5
+ import type { DateRange } from 'react-day-picker'
6
+
7
+ import { Button } from '../button/button.js'
8
+ import { Calendar as CalendarComponent } from './calendar.js'
9
+
10
+ const meta: Meta<typeof CalendarComponent> = {
11
+ /* 👇 The title prop is optional.
12
+ * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
13
+ * to learn how to generate automatic titles
14
+ */
15
+ title: 'Components/Inputs/Calendar',
16
+ component: CalendarComponent,
17
+ }
18
+
19
+ export default meta
20
+
21
+ export const Calendar = (): React.JSX.Element => {
22
+ const [_isOpen, setIsOpen] = useState(false)
23
+ const [time, _setTime] = useState<string>('05:00')
24
+ const [date, setDate] = useState<Date>(new Date())
25
+ const [month, setMonth] = useState<Date>(date)
26
+
27
+ const calendarRef = useRef<HTMLDivElement | null>(null)
28
+
29
+ return (
30
+ <div style={{ height: '100vh', margin: '2rem 0' }}>
31
+ <div ref={calendarRef} style={{ position: 'relative', width: 'fit-content', margin: 'auto' }}>
32
+ <CalendarComponent
33
+ required
34
+ mode="single"
35
+ captionLayout="dropdown"
36
+ selected={date}
37
+ month={month}
38
+ onMonthChange={setMonth}
39
+ onSelect={(selectedDate: Date) => {
40
+ if (selectedDate) {
41
+ const [hours, minutes] = time.split(':')
42
+ selectedDate.setHours(Number.parseInt(hours, 10), Number.parseInt(minutes, 10))
43
+ setDate(selectedDate)
44
+ setMonth(selectedDate)
45
+ }
46
+ }}
47
+ onDayClick={() => setIsOpen(false)}
48
+ // fromYear={2000}
49
+ // toYear={new Date().getFullYear()}
50
+ // disabled={(date: Date) =>
51
+ // Number(date) < Date.now() - 1000 * 60 * 60 * 24 ||
52
+ // Number(date) > Date.now() + 1000 * 60 * 60 * 24 * 30
53
+ // }
54
+ />
55
+ <div style={{ paddingLeft: '1rem', paddingTop: '1rem' }}>
56
+ {date ? `${format(date, 'PPPp')}` : 'No date selected'}
57
+ </div>
58
+ <div style={{ paddingLeft: '1rem', paddingTop: '1rem' }}>
59
+ <Button
60
+ size="sm"
61
+ intent="noeffect"
62
+ style={{ marginTop: '0.5rem' }}
63
+ onClick={() => {
64
+ const today = new Date()
65
+ setDate(today)
66
+ setMonth(today)
67
+ }}
68
+ >
69
+ Today
70
+ </Button>
71
+ </div>
72
+ </div>
73
+ </div>
74
+ )
75
+ }
76
+
77
+ export const Multiple = (): React.JSX.Element => {
78
+ const [_isOpen, setIsOpen] = useState(false)
79
+ const [dates, setDates] = useState<Date[] | undefined>([new Date()])
80
+
81
+ const calendarRef = useRef<HTMLDivElement | null>(null)
82
+
83
+ return (
84
+ <div style={{ height: '100vh', margin: '2rem 0' }}>
85
+ <div ref={calendarRef} style={{ position: 'relative', width: 'fit-content', margin: 'auto' }}>
86
+ <CalendarComponent
87
+ required
88
+ mode="multiple"
89
+ captionLayout="dropdown"
90
+ selected={dates}
91
+ onSelect={(selectedDates: Date[]) => {
92
+ if (selectedDates) {
93
+ // selectedDate.setHours(Number.parseInt(hours), Number.parseInt(minutes))
94
+ setDates(selectedDates)
95
+ }
96
+ }}
97
+ onDayClick={() => setIsOpen(false)}
98
+ // fromYear={2000}
99
+ // toYear={new Date().getFullYear()}
100
+ // disabled={(date: Date) =>
101
+ // Number(date) < Date.now() - 1000 * 60 * 60 * 24 ||
102
+ // Number(date) > Date.now() + 1000 * 60 * 60 * 24 * 30
103
+ // }
104
+ />
105
+ </div>
106
+ </div>
107
+ )
108
+ }
109
+
110
+ export const Range = (): React.JSX.Element => {
111
+ const [_isOpen, setIsOpen] = useState(false)
112
+ const [dateRange, setDateRange] = useState<DateRange | undefined>(undefined)
113
+
114
+ const calendarRef = useRef<HTMLDivElement | null>(null)
115
+
116
+ return (
117
+ <div style={{ height: '100vh', margin: '2rem 0' }}>
118
+ <div ref={calendarRef} style={{ position: 'relative', width: 'fit-content', margin: 'auto' }}>
119
+ <CalendarComponent
120
+ required
121
+ mode="range"
122
+ captionLayout="dropdown"
123
+ selected={dateRange}
124
+ onSelect={(selectedDateRange: DateRange) => {
125
+ if (selectedDateRange) {
126
+ // selectedDate.setHours(Number.parseInt(hours), Number.parseInt(minutes))
127
+ setDateRange(selectedDateRange)
128
+ }
129
+ }}
130
+ onDayClick={() => setIsOpen(false)}
131
+ // fromYear={2000}
132
+ // toYear={new Date().getFullYear()}
133
+ // disabled={(date: Date) =>
134
+ // Number(date) < Date.now() - 1000 * 60 * 60 * 24 ||
135
+ // Number(date) > Date.now() + 1000 * 60 * 60 * 24 * 30
136
+ // }
137
+ />
138
+ </div>
139
+ </div>
140
+ )
141
+ }
@@ -0,0 +1,43 @@
1
+ import type React from 'react'
2
+ import { useState } from 'react'
3
+
4
+ import { CheckboxGroup } from './checkbox-group.js'
5
+
6
+ export default {
7
+ title: 'Components/Inputs/CheckboxGroup',
8
+ component: CheckboxGroup,
9
+ argTypes: {},
10
+ }
11
+
12
+ export const Default = (): React.JSX.Element => {
13
+ const [checked, setChecked] = useState(false)
14
+
15
+ const _handleOnClick = (): void => {
16
+ console.log('clicked')
17
+ setChecked(!checked)
18
+ }
19
+
20
+ return (
21
+ <div style={{ maxWidth: '800px', margin: '2rem auto 2rem' }}>
22
+ <h2 style={{ fontSize: '1.25rem', marginBottom: '1rem' }}>Checkbox Group</h2>
23
+ <div
24
+ style={{ display: 'grid', gridTemplateColumns: 'repeat(3, minmax(0, 1fr))', gap: '1rem' }}
25
+ >
26
+ <div style={{ minWidth: 0 }}>
27
+ <CheckboxGroup
28
+ groupName="roles"
29
+ // TODO: Nullability of role.name
30
+ checkBoxes={[
31
+ { id: 'admin', label: 'Admin' },
32
+ { id: 'editor', label: 'Editor' },
33
+ { id: 'viewer', label: 'Viewer' },
34
+ ]}
35
+ // onChange={(selected) => {
36
+ // handleOnClick(selected)
37
+ // }}
38
+ />
39
+ </div>
40
+ </div>
41
+ </div>
42
+ )
43
+ }