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.
Files changed (64) hide show
  1. package/lib/assets/IconsSvg.d.ts +1 -2
  2. package/lib/components/Button/Button.d.ts +6 -6
  3. package/lib/components/FilterBar/FilterBar.d.ts +1 -2
  4. package/lib/components/FilterBar/FilterBarTypes.d.ts +3 -13
  5. package/lib/components/FilterBar/components/buttons/select-button/SelectButton.d.ts +1 -2
  6. package/lib/components/FilterBar/components/buttons/tab-button/TabButton.d.ts +1 -2
  7. package/lib/components/FilterBar/components/cards/image-card/ImageCard.d.ts +1 -2
  8. package/lib/components/FilterBar/components/categories/Categories.d.ts +1 -2
  9. package/lib/components/FilterBar/components/common/FilterSectionHeader.d.ts +2 -2
  10. package/lib/components/FilterBar/components/dates/Dates.d.ts +5 -4
  11. package/lib/components/FilterBar/components/divider/Divider.d.ts +1 -2
  12. package/lib/components/FilterBar/components/guests/Guests.d.ts +1 -2
  13. package/lib/components/FilterBar/components/locations/Locations.d.ts +1 -2
  14. package/lib/components/FilterBar/utils/index.d.ts +1 -1
  15. package/lib/components/FilterBar/utils/parseLocations.d.ts +1 -2
  16. package/lib/components/FilterCalendar/FilterCalendar.d.ts +1 -2
  17. package/lib/components/FilterCalendar/components/Footer.d.ts +1 -2
  18. package/lib/core/components/buttons/close-button/CloseButton.d.ts +1 -2
  19. package/lib/core/components/buttons/submit-button/SubmitButton.d.ts +2 -2
  20. package/lib/core/components/calendar/Calendar.d.ts +1 -2
  21. package/lib/index.d.ts +11 -19
  22. package/lib/index.esm.js +346 -359
  23. package/lib/index.esm.js.map +1 -1
  24. package/lib/index.js +431 -444
  25. package/lib/index.js.map +1 -1
  26. package/lib/index.umd.js +433 -447
  27. package/lib/index.umd.js.map +1 -1
  28. package/package.json +1 -1
  29. package/rollup.config.mjs +1 -1
  30. package/src/assets/IconsSvg.tsx +0 -2
  31. package/src/components/Button/Button.stories.tsx +15 -16
  32. package/src/components/Button/Button.tsx +15 -15
  33. package/src/components/FilterBar/FilterBar.css +1 -1
  34. package/src/components/FilterBar/FilterBar.stories.tsx +14 -50
  35. package/src/components/FilterBar/FilterBar.tsx +44 -13
  36. package/src/components/FilterBar/FilterBarTypes.ts +3 -14
  37. package/src/components/FilterBar/components/buttons/select-button/SelectButton.tsx +13 -2
  38. package/src/components/FilterBar/components/buttons/tab-button/TabButton.tsx +0 -2
  39. package/src/components/FilterBar/components/cards/image-card/ImageCard.css +0 -1
  40. package/src/components/FilterBar/components/cards/image-card/ImageCard.tsx +1 -1
  41. package/src/components/FilterBar/components/categories/Categories.tsx +2 -1
  42. package/src/components/FilterBar/components/common/FilterSectionHeader.css +1 -0
  43. package/src/components/FilterBar/components/common/FilterSectionHeader.tsx +2 -1
  44. package/src/components/FilterBar/components/dates/Dates.css +3 -0
  45. package/src/components/FilterBar/components/dates/Dates.tsx +5 -4
  46. package/src/components/FilterBar/components/divider/Divider.tsx +0 -2
  47. package/src/components/FilterBar/components/guests/GuestCount/GuestCount.tsx +1 -1
  48. package/src/components/FilterBar/components/guests/Guests.css +1 -1
  49. package/src/components/FilterBar/components/guests/Guests.tsx +11 -2
  50. package/src/components/FilterBar/components/locations/Locations.css +1 -1
  51. package/src/components/FilterBar/components/locations/Locations.tsx +16 -36
  52. package/src/components/FilterBar/utils/calculateDropdownPosition.tsx +106 -0
  53. package/src/components/FilterBar/utils/index.tsx +1 -1
  54. package/src/components/FilterBar/utils/parseLocations.tsx +3 -7
  55. package/src/components/FilterCalendar/FilterCalendar.stories.tsx +1 -1
  56. package/src/components/FilterCalendar/FilterCalendar.tsx +0 -1
  57. package/src/components/FilterCalendar/components/Footer.tsx +0 -1
  58. package/src/core/components/buttons/close-button/CloseButton.tsx +0 -1
  59. package/src/core/components/buttons/submit-button/SubmitButton.tsx +1 -1
  60. package/src/core/components/calendar/Calendar.tsx +7 -4
  61. package/stories/Button.tsx +14 -11
  62. package/stories/Header.tsx +27 -14
  63. package/stories/Page.tsx +39 -21
  64. 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,3 +1,3 @@
1
1
  export { parseGuests } from './parseGuests'
2
2
  export { parseLocations } from './parseLocations'
3
- export { getLocalizedContent } from './getLocalizedContent'
3
+ export { calculateDropdownPosition } from './calculateDropdownPosition'
@@ -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
- language,
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 = getLocalizedContent({
23
- contents: selectedLocations[0].label,
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 React, { useState } from 'react'
1
+ import { useState } from 'react'
2
2
  import type { Meta, StoryObj } from '@storybook/react'
3
3
 
4
4
  import FilterCalendar from './FilterCalendar'
@@ -1,4 +1,3 @@
1
- import React from 'react'
2
1
  import { useTranslation } from 'react-i18next'
3
2
 
4
3
  import useTheme from '../../themes/useTheme'
@@ -1,4 +1,3 @@
1
- import React from 'react'
2
1
  import { useTranslation } from 'react-i18next'
3
2
  import { useMediaQuery } from 'react-responsive'
4
3
  import { DateRange } from 'react-day-picker'
@@ -1,4 +1,3 @@
1
- import React from 'react'
2
1
  import { IoIosCloseCircleOutline } from 'react-icons/io'
3
2
 
4
3
  import './CloseButton.css'
@@ -1,5 +1,5 @@
1
1
  import { FaSpinner } from 'react-icons/fa'
2
- import React, { ReactNode } from 'react'
2
+ import { ReactNode } from 'react'
3
3
 
4
4
  import './SubmitButton.css'
5
5
 
@@ -1,4 +1,4 @@
1
- import React, { forwardRef } from 'react'
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
- ? disabledDates
151
- : newDisableCalendarDates?.disabledDates || []
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 className="will-calendar-filter-container" ref={calendarContainerRef}>
165
+ <div
166
+ className="will-calendar-filter-container"
167
+ ref={calendarContainerRef}
168
+ >
166
169
  <DayPicker
167
170
  key={updateCalendarDefaultMonth}
168
171
  id="will-calendar"
@@ -1,27 +1,26 @@
1
- import React from 'react';
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 ? 'storybook-button--primary' : 'storybook-button--secondary';
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
+ }
@@ -1,24 +1,32 @@
1
- import React from 'react';
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 = ({ user, onLogin, onLogout, onCreateAccount }: HeaderProps) => (
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 width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/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 primary size="small" onClick={onCreateAccount} label="Sign up" />
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 React from 'react';
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: React.FC = () => {
11
- const [user, setUser] = React.useState<User>();
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 href="https://componentdriven.org" target="_blank" rel="noopener noreferrer">
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 page states without
33
- needing to navigate to them in your app. Here are some handy patterns for managing page
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 such data from the
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 these services out
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 href="https://storybook.js.org/tutorials/" target="_blank" rel="noopener noreferrer">
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 href="https://storybook.js.org/docs" target="_blank" rel="noopener noreferrer">
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 the{' '}
59
- <svg width="10" height="10" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
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
- }