willba-component-library 0.3.10 → 0.3.12
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/lib/assets/IconsSvg.d.ts +1 -2
- package/lib/components/Button/Button.d.ts +6 -6
- package/lib/components/FilterBar/FilterBar.d.ts +1 -2
- package/lib/components/FilterBar/FilterBarTypes.d.ts +3 -13
- package/lib/components/FilterBar/components/buttons/select-button/SelectButton.d.ts +1 -2
- package/lib/components/FilterBar/components/buttons/tab-button/TabButton.d.ts +1 -2
- package/lib/components/FilterBar/components/cards/image-card/ImageCard.d.ts +1 -2
- package/lib/components/FilterBar/components/categories/Categories.d.ts +1 -2
- package/lib/components/FilterBar/components/common/FilterSectionHeader.d.ts +2 -2
- package/lib/components/FilterBar/components/dates/Dates.d.ts +5 -4
- package/lib/components/FilterBar/components/divider/Divider.d.ts +1 -2
- package/lib/components/FilterBar/components/guests/Guests.d.ts +1 -2
- package/lib/components/FilterBar/components/locations/Locations.d.ts +1 -2
- package/lib/components/FilterBar/utils/index.d.ts +1 -1
- package/lib/components/FilterBar/utils/parseLocations.d.ts +1 -2
- package/lib/components/FilterCalendar/FilterCalendar.d.ts +1 -2
- package/lib/components/FilterCalendar/components/Footer.d.ts +1 -2
- package/lib/core/components/buttons/close-button/CloseButton.d.ts +1 -2
- package/lib/core/components/buttons/submit-button/SubmitButton.d.ts +2 -2
- package/lib/core/components/calendar/Calendar.d.ts +1 -2
- package/lib/index.d.ts +11 -19
- package/lib/index.esm.js +346 -359
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +431 -444
- package/lib/index.js.map +1 -1
- package/lib/index.umd.js +433 -447
- package/lib/index.umd.js.map +1 -1
- package/package.json +1 -1
- package/rollup.config.mjs +1 -1
- package/src/assets/IconsSvg.tsx +0 -2
- package/src/components/Button/Button.stories.tsx +15 -16
- package/src/components/Button/Button.tsx +15 -15
- package/src/components/FilterBar/FilterBar.css +1 -1
- package/src/components/FilterBar/FilterBar.stories.tsx +14 -50
- package/src/components/FilterBar/FilterBar.tsx +44 -13
- package/src/components/FilterBar/FilterBarTypes.ts +3 -14
- package/src/components/FilterBar/components/buttons/select-button/SelectButton.tsx +13 -2
- package/src/components/FilterBar/components/buttons/tab-button/TabButton.tsx +0 -2
- package/src/components/FilterBar/components/cards/image-card/ImageCard.css +0 -1
- package/src/components/FilterBar/components/cards/image-card/ImageCard.tsx +1 -1
- package/src/components/FilterBar/components/categories/Categories.tsx +2 -1
- package/src/components/FilterBar/components/common/FilterSectionHeader.css +1 -0
- package/src/components/FilterBar/components/common/FilterSectionHeader.tsx +2 -1
- package/src/components/FilterBar/components/dates/Dates.css +3 -0
- package/src/components/FilterBar/components/dates/Dates.tsx +5 -4
- package/src/components/FilterBar/components/divider/Divider.tsx +0 -2
- package/src/components/FilterBar/components/guests/GuestCount/GuestCount.tsx +1 -1
- package/src/components/FilterBar/components/guests/Guests.css +1 -1
- package/src/components/FilterBar/components/guests/Guests.tsx +11 -2
- package/src/components/FilterBar/components/locations/Locations.css +1 -1
- package/src/components/FilterBar/components/locations/Locations.tsx +16 -36
- package/src/components/FilterBar/utils/calculateDropdownPosition.tsx +106 -0
- package/src/components/FilterBar/utils/index.tsx +1 -1
- package/src/components/FilterBar/utils/parseLocations.tsx +3 -7
- package/src/components/FilterCalendar/FilterCalendar.stories.tsx +1 -1
- package/src/components/FilterCalendar/FilterCalendar.tsx +0 -1
- package/src/components/FilterCalendar/components/Footer.tsx +0 -1
- package/src/core/components/buttons/close-button/CloseButton.tsx +0 -1
- package/src/core/components/buttons/submit-button/SubmitButton.tsx +1 -1
- package/src/core/components/calendar/Calendar.tsx +7 -4
- package/stories/Button.tsx +14 -11
- package/stories/Header.tsx +27 -14
- package/stories/Page.tsx +39 -21
- package/src/components/FilterBar/utils/getLocalizedContent.tsx +0 -21
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { CSSProperties, RefObject } from 'react'
|
|
2
|
+
import { FilterSections } from '../FilterBarTypes'
|
|
3
|
+
|
|
4
|
+
type CalculateDropdownPositionParams = {
|
|
5
|
+
filterSection: FilterSections
|
|
6
|
+
headerRef: RefObject<HTMLDivElement>
|
|
7
|
+
locationsButtonRef: RefObject<HTMLButtonElement>
|
|
8
|
+
datesButtonRef: RefObject<HTMLButtonElement>
|
|
9
|
+
guestsButtonRef: RefObject<HTMLButtonElement>
|
|
10
|
+
isMobile: boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const calculateDropdownPosition = ({
|
|
14
|
+
filterSection,
|
|
15
|
+
headerRef,
|
|
16
|
+
locationsButtonRef,
|
|
17
|
+
datesButtonRef,
|
|
18
|
+
guestsButtonRef,
|
|
19
|
+
isMobile,
|
|
20
|
+
}: CalculateDropdownPositionParams): CSSProperties => {
|
|
21
|
+
// On mobile, don't apply any positioning - let CSS handle it naturally
|
|
22
|
+
// Dropdowns will start from leftmost point with position: relative
|
|
23
|
+
if (isMobile) {
|
|
24
|
+
return {}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!headerRef.current) return {}
|
|
28
|
+
|
|
29
|
+
const containerRect = headerRef.current.getBoundingClientRect()
|
|
30
|
+
const containerLeft = 0
|
|
31
|
+
|
|
32
|
+
switch (filterSection) {
|
|
33
|
+
case FilterSections.LOCATIONS:
|
|
34
|
+
// Locations: Start from beginning, hug content
|
|
35
|
+
if (locationsButtonRef.current) {
|
|
36
|
+
const buttonRect = locationsButtonRef.current.getBoundingClientRect()
|
|
37
|
+
const relativeLeft = buttonRect.left - containerRect.left
|
|
38
|
+
return {
|
|
39
|
+
left: relativeLeft,
|
|
40
|
+
right: 'auto',
|
|
41
|
+
width: 'auto',
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
break
|
|
45
|
+
|
|
46
|
+
case FilterSections.CALENDAR:
|
|
47
|
+
// Calendar: Two months side-by-side, needs ~650-700px
|
|
48
|
+
// Start from dates button, but push left if not enough space
|
|
49
|
+
if (datesButtonRef.current) {
|
|
50
|
+
const buttonRect = datesButtonRef.current.getBoundingClientRect()
|
|
51
|
+
const relativeLeft = buttonRect.left - containerRect.left
|
|
52
|
+
const availableWidth = containerRect.width - relativeLeft
|
|
53
|
+
const calendarMinWidth = 650
|
|
54
|
+
|
|
55
|
+
if (availableWidth < calendarMinWidth) {
|
|
56
|
+
// Not enough space, align to the right edge
|
|
57
|
+
return {
|
|
58
|
+
left: 'auto',
|
|
59
|
+
right: containerLeft,
|
|
60
|
+
width: 'auto',
|
|
61
|
+
maxWidth: `${containerRect.width}px`,
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
// Enough space, start from dates button
|
|
65
|
+
return {
|
|
66
|
+
left: relativeLeft,
|
|
67
|
+
right: 'auto',
|
|
68
|
+
width: 'auto',
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
break
|
|
73
|
+
|
|
74
|
+
case FilterSections.GUESTS:
|
|
75
|
+
// Guests: Start from guests button, push left if not enough space
|
|
76
|
+
if (guestsButtonRef.current) {
|
|
77
|
+
const buttonRect = guestsButtonRef.current.getBoundingClientRect()
|
|
78
|
+
const relativeLeft = buttonRect.left - containerRect.left
|
|
79
|
+
const availableWidth = containerRect.width - relativeLeft
|
|
80
|
+
const dropdownMinWidth = 350
|
|
81
|
+
|
|
82
|
+
if (availableWidth < dropdownMinWidth) {
|
|
83
|
+
// Not enough space, align to the right
|
|
84
|
+
return {
|
|
85
|
+
left: 'auto',
|
|
86
|
+
right: containerLeft,
|
|
87
|
+
width: 'auto',
|
|
88
|
+
maxWidth: `${containerRect.width}px`,
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
// Enough space, start from button
|
|
92
|
+
return {
|
|
93
|
+
left: relativeLeft,
|
|
94
|
+
right: 'auto',
|
|
95
|
+
width: 'auto',
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
break
|
|
100
|
+
|
|
101
|
+
default:
|
|
102
|
+
return {}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {}
|
|
106
|
+
}
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import { Location } from '../FilterBarTypes'
|
|
2
|
-
import { getLocalizedContent } from './getLocalizedContent'
|
|
3
2
|
|
|
4
3
|
type Props = {
|
|
5
4
|
selectedLocations: Location[]
|
|
6
|
-
language: string
|
|
7
5
|
locationsPlaceholder: string
|
|
8
6
|
locationsSelectedLabel?: string
|
|
9
7
|
}
|
|
10
8
|
|
|
11
9
|
export const parseLocations = ({
|
|
12
10
|
selectedLocations,
|
|
13
|
-
|
|
11
|
+
|
|
14
12
|
locationsPlaceholder,
|
|
15
13
|
locationsSelectedLabel = 'locations',
|
|
16
14
|
}: Props) => {
|
|
@@ -19,10 +17,8 @@ export const parseLocations = ({
|
|
|
19
17
|
}
|
|
20
18
|
|
|
21
19
|
if (selectedLocations.length === 1) {
|
|
22
|
-
const translation =
|
|
23
|
-
|
|
24
|
-
locale: language,
|
|
25
|
-
})
|
|
20
|
+
const translation = selectedLocations[0].label
|
|
21
|
+
|
|
26
22
|
if (!translation) {
|
|
27
23
|
return locationsPlaceholder
|
|
28
24
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { forwardRef } from 'react'
|
|
2
2
|
import { addDays, startOfDay } from 'date-fns'
|
|
3
3
|
import { fi, enUS } from 'date-fns/locale'
|
|
4
4
|
import { useTranslation } from 'react-i18next'
|
|
@@ -147,8 +147,8 @@ export const Calendar = forwardRef<HTMLDivElement, CalendarTypes>(
|
|
|
147
147
|
const base = disabledDatesByPage.length
|
|
148
148
|
? disabledDatesByPage
|
|
149
149
|
: disabledDates?.length
|
|
150
|
-
|
|
151
|
-
|
|
150
|
+
? disabledDates
|
|
151
|
+
: newDisableCalendarDates?.disabledDates || []
|
|
152
152
|
|
|
153
153
|
const disabled = disabledDatesByPage.length
|
|
154
154
|
? base
|
|
@@ -162,7 +162,10 @@ export const Calendar = forwardRef<HTMLDivElement, CalendarTypes>(
|
|
|
162
162
|
|
|
163
163
|
return (
|
|
164
164
|
<div className="will-filter-bar-calendar" ref={ref}>
|
|
165
|
-
<div
|
|
165
|
+
<div
|
|
166
|
+
className="will-calendar-filter-container"
|
|
167
|
+
ref={calendarContainerRef}
|
|
168
|
+
>
|
|
166
169
|
<DayPicker
|
|
167
170
|
key={updateCalendarDefaultMonth}
|
|
168
171
|
id="will-calendar"
|
package/stories/Button.tsx
CHANGED
|
@@ -1,27 +1,26 @@
|
|
|
1
|
-
import
|
|
2
|
-
import './button.css';
|
|
1
|
+
import './button.css'
|
|
3
2
|
|
|
4
3
|
interface ButtonProps {
|
|
5
4
|
/**
|
|
6
5
|
* Is this the principal call to action on the page?
|
|
7
6
|
*/
|
|
8
|
-
primary?: boolean
|
|
7
|
+
primary?: boolean
|
|
9
8
|
/**
|
|
10
9
|
* What background color to use
|
|
11
10
|
*/
|
|
12
|
-
backgroundColor?: string
|
|
11
|
+
backgroundColor?: string
|
|
13
12
|
/**
|
|
14
13
|
* How large should the button be?
|
|
15
14
|
*/
|
|
16
|
-
size?: 'small' | 'medium' | 'large'
|
|
15
|
+
size?: 'small' | 'medium' | 'large'
|
|
17
16
|
/**
|
|
18
17
|
* Button contents
|
|
19
18
|
*/
|
|
20
|
-
label: string
|
|
19
|
+
label: string
|
|
21
20
|
/**
|
|
22
21
|
* Optional click handler
|
|
23
22
|
*/
|
|
24
|
-
onClick?: () => void
|
|
23
|
+
onClick?: () => void
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
/**
|
|
@@ -34,15 +33,19 @@ export const Button = ({
|
|
|
34
33
|
label,
|
|
35
34
|
...props
|
|
36
35
|
}: ButtonProps) => {
|
|
37
|
-
const mode = primary
|
|
36
|
+
const mode = primary
|
|
37
|
+
? 'storybook-button--primary'
|
|
38
|
+
: 'storybook-button--secondary'
|
|
38
39
|
return (
|
|
39
40
|
<button
|
|
40
41
|
type="button"
|
|
41
|
-
className={['storybook-button', `storybook-button--${size}`, mode].join(
|
|
42
|
+
className={['storybook-button', `storybook-button--${size}`, mode].join(
|
|
43
|
+
' '
|
|
44
|
+
)}
|
|
42
45
|
style={{ backgroundColor }}
|
|
43
46
|
{...props}
|
|
44
47
|
>
|
|
45
48
|
{label}
|
|
46
49
|
</button>
|
|
47
|
-
)
|
|
48
|
-
}
|
|
50
|
+
)
|
|
51
|
+
}
|
package/stories/Header.tsx
CHANGED
|
@@ -1,24 +1,32 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import { Button } from './Button';
|
|
4
|
-
import './header.css';
|
|
1
|
+
import { Button } from './Button'
|
|
2
|
+
import './header.css'
|
|
5
3
|
|
|
6
4
|
type User = {
|
|
7
|
-
name: string
|
|
8
|
-
}
|
|
5
|
+
name: string
|
|
6
|
+
}
|
|
9
7
|
|
|
10
8
|
interface HeaderProps {
|
|
11
|
-
user?: User
|
|
12
|
-
onLogin: () => void
|
|
13
|
-
onLogout: () => void
|
|
14
|
-
onCreateAccount: () => void
|
|
9
|
+
user?: User
|
|
10
|
+
onLogin: () => void
|
|
11
|
+
onLogout: () => void
|
|
12
|
+
onCreateAccount: () => void
|
|
15
13
|
}
|
|
16
14
|
|
|
17
|
-
export const Header = ({
|
|
15
|
+
export const Header = ({
|
|
16
|
+
user,
|
|
17
|
+
onLogin,
|
|
18
|
+
onLogout,
|
|
19
|
+
onCreateAccount,
|
|
20
|
+
}: HeaderProps) => (
|
|
18
21
|
<header>
|
|
19
22
|
<div className="storybook-header">
|
|
20
23
|
<div>
|
|
21
|
-
<svg
|
|
24
|
+
<svg
|
|
25
|
+
width="32"
|
|
26
|
+
height="32"
|
|
27
|
+
viewBox="0 0 32 32"
|
|
28
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
29
|
+
>
|
|
22
30
|
<g fill="none" fillRule="evenodd">
|
|
23
31
|
<path
|
|
24
32
|
d="M10 0h12a10 10 0 0110 10v12a10 10 0 01-10 10H10A10 10 0 010 22V10A10 10 0 0110 0z"
|
|
@@ -47,10 +55,15 @@ export const Header = ({ user, onLogin, onLogout, onCreateAccount }: HeaderProps
|
|
|
47
55
|
) : (
|
|
48
56
|
<>
|
|
49
57
|
<Button size="small" onClick={onLogin} label="Log in" />
|
|
50
|
-
<Button
|
|
58
|
+
<Button
|
|
59
|
+
primary
|
|
60
|
+
size="small"
|
|
61
|
+
onClick={onCreateAccount}
|
|
62
|
+
label="Sign up"
|
|
63
|
+
/>
|
|
51
64
|
</>
|
|
52
65
|
)}
|
|
53
66
|
</div>
|
|
54
67
|
</div>
|
|
55
68
|
</header>
|
|
56
|
-
)
|
|
69
|
+
)
|
package/stories/Page.tsx
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { FC, useState } from 'react'
|
|
2
2
|
|
|
3
|
-
import { Header } from './Header'
|
|
4
|
-
import './page.css'
|
|
3
|
+
import { Header } from './Header'
|
|
4
|
+
import './page.css'
|
|
5
5
|
|
|
6
6
|
type User = {
|
|
7
|
-
name: string
|
|
8
|
-
}
|
|
7
|
+
name: string
|
|
8
|
+
}
|
|
9
9
|
|
|
10
|
-
export const Page:
|
|
11
|
-
const [user, setUser] =
|
|
10
|
+
export const Page: FC = () => {
|
|
11
|
+
const [user, setUser] = useState<User>()
|
|
12
12
|
|
|
13
13
|
return (
|
|
14
14
|
<article>
|
|
@@ -23,40 +23,58 @@ export const Page: React.FC = () => {
|
|
|
23
23
|
<h2>Pages in Storybook</h2>
|
|
24
24
|
<p>
|
|
25
25
|
We recommend building UIs with a{' '}
|
|
26
|
-
<a
|
|
26
|
+
<a
|
|
27
|
+
href="https://componentdriven.org"
|
|
28
|
+
target="_blank"
|
|
29
|
+
rel="noopener noreferrer"
|
|
30
|
+
>
|
|
27
31
|
<strong>component-driven</strong>
|
|
28
32
|
</a>{' '}
|
|
29
33
|
process starting with atomic components and ending with pages.
|
|
30
34
|
</p>
|
|
31
35
|
<p>
|
|
32
|
-
Render pages with mock data. This makes it easy to build and review
|
|
33
|
-
needing to navigate to them in your app. Here are
|
|
34
|
-
data in Storybook:
|
|
36
|
+
Render pages with mock data. This makes it easy to build and review
|
|
37
|
+
page states without needing to navigate to them in your app. Here are
|
|
38
|
+
some handy patterns for managing page data in Storybook:
|
|
35
39
|
</p>
|
|
36
40
|
<ul>
|
|
37
41
|
<li>
|
|
38
|
-
Use a higher-level connected component. Storybook helps you compose
|
|
39
|
-
"args" of child component stories
|
|
42
|
+
Use a higher-level connected component. Storybook helps you compose
|
|
43
|
+
such data from the "args" of child component stories
|
|
40
44
|
</li>
|
|
41
45
|
<li>
|
|
42
|
-
Assemble data in the page component from your services. You can mock
|
|
43
|
-
using Storybook.
|
|
46
|
+
Assemble data in the page component from your services. You can mock
|
|
47
|
+
these services out using Storybook.
|
|
44
48
|
</li>
|
|
45
49
|
</ul>
|
|
46
50
|
<p>
|
|
47
51
|
Get a guided tutorial on component-driven development at{' '}
|
|
48
|
-
<a
|
|
52
|
+
<a
|
|
53
|
+
href="https://storybook.js.org/tutorials/"
|
|
54
|
+
target="_blank"
|
|
55
|
+
rel="noopener noreferrer"
|
|
56
|
+
>
|
|
49
57
|
Storybook tutorials
|
|
50
58
|
</a>
|
|
51
59
|
. Read more in the{' '}
|
|
52
|
-
<a
|
|
60
|
+
<a
|
|
61
|
+
href="https://storybook.js.org/docs"
|
|
62
|
+
target="_blank"
|
|
63
|
+
rel="noopener noreferrer"
|
|
64
|
+
>
|
|
53
65
|
docs
|
|
54
66
|
</a>
|
|
55
67
|
.
|
|
56
68
|
</p>
|
|
57
69
|
<div className="tip-wrapper">
|
|
58
|
-
<span className="tip">Tip</span> Adjust the width of the canvas with
|
|
59
|
-
|
|
70
|
+
<span className="tip">Tip</span> Adjust the width of the canvas with
|
|
71
|
+
the{' '}
|
|
72
|
+
<svg
|
|
73
|
+
width="10"
|
|
74
|
+
height="10"
|
|
75
|
+
viewBox="0 0 12 12"
|
|
76
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
77
|
+
>
|
|
60
78
|
<g fill="none" fillRule="evenodd">
|
|
61
79
|
<path
|
|
62
80
|
d="M1.5 5.2h4.8c.3 0 .5.2.5.4v5.1c-.1.2-.3.3-.4.3H1.4a.5.5 0 01-.5-.4V5.7c0-.3.2-.5.5-.5zm0-2.1h6.9c.3 0 .5.2.5.4v7a.5.5 0 01-1 0V4H1.5a.5.5 0 010-1zm0-2.1h9c.3 0 .5.2.5.4v9.1a.5.5 0 01-1 0V2H1.5a.5.5 0 010-1zm4.3 5.2H2V10h3.8V6.2z"
|
|
@@ -69,5 +87,5 @@ export const Page: React.FC = () => {
|
|
|
69
87
|
</div>
|
|
70
88
|
</section>
|
|
71
89
|
</article>
|
|
72
|
-
)
|
|
73
|
-
}
|
|
90
|
+
)
|
|
91
|
+
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { LocaleTranslation } from '../FilterBarTypes'
|
|
2
|
-
|
|
3
|
-
type Props = {
|
|
4
|
-
contents: LocaleTranslation
|
|
5
|
-
locale: string
|
|
6
|
-
fallbackLocale?: string
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export const getLocalizedContent = ({
|
|
10
|
-
contents,
|
|
11
|
-
locale,
|
|
12
|
-
fallbackLocale = 'en',
|
|
13
|
-
}: Props): string | undefined => {
|
|
14
|
-
const preferred = contents.find((content) => content.locale === locale)
|
|
15
|
-
if (preferred) return preferred.content
|
|
16
|
-
|
|
17
|
-
const fallback = contents.find((content) => content.locale === fallbackLocale)
|
|
18
|
-
if (fallback) return fallback.content
|
|
19
|
-
|
|
20
|
-
return contents[0]?.content
|
|
21
|
-
}
|