@homefile/components-v2 2.62.0 → 2.63.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 (34) hide show
  1. package/dist/components/onboarding/PageTitle.d.ts +1 -1
  2. package/dist/components/onboarding/PageTitle.js +5 -3
  3. package/dist/components/onboarding/pages/MessagePage.d.ts +2 -0
  4. package/dist/components/onboarding/pages/MessagePage.js +11 -0
  5. package/dist/components/onboarding/pages/SignIn.d.ts +1 -1
  6. package/dist/components/onboarding/pages/SignIn.js +3 -3
  7. package/dist/components/onboarding/pages/TwoFactor.d.ts +1 -1
  8. package/dist/components/onboarding/pages/TwoFactor.js +5 -3
  9. package/dist/components/onboarding/pages/index.d.ts +1 -0
  10. package/dist/components/onboarding/pages/index.js +1 -0
  11. package/dist/index.d.ts +1 -1
  12. package/dist/index.js +1 -1
  13. package/dist/interfaces/onboarding/PageTitle.interface.d.ts +1 -0
  14. package/dist/interfaces/pages/MessagePage.interface.d.ts +6 -0
  15. package/dist/interfaces/pages/MessagePage.interface.js +1 -0
  16. package/dist/interfaces/pages/SignIn.interface.d.ts +3 -1
  17. package/dist/interfaces/pages/TwoFactor.interface.d.ts +5 -0
  18. package/dist/interfaces/pages/index.d.ts +1 -0
  19. package/dist/interfaces/pages/index.js +1 -0
  20. package/dist/stories/onboarding/PageTitle.stories.d.ts +2 -1
  21. package/dist/stories/onboarding/PageTitle.stories.js +31 -0
  22. package/dist/stories/onboarding/pages/MessagePage.stories.d.ts +9 -0
  23. package/dist/stories/onboarding/pages/MessagePage.stories.js +65 -0
  24. package/dist/stories/onboarding/pages/SignIn.stories.d.ts +8 -1
  25. package/dist/stories/onboarding/pages/SignIn.stories.js +44 -0
  26. package/dist/stories/onboarding/pages/TwoFactor.stories.d.ts +9 -2
  27. package/dist/stories/onboarding/pages/TwoFactor.stories.js +58 -0
  28. package/dist/stories/partner/adminTiles/InboxCard.stories.d.ts +1 -1
  29. package/dist/stories/partner/adminTiles/InboxCard.stories.js +8 -5
  30. package/dist/stories/typography/NoWidow.stories.d.ts +8 -0
  31. package/dist/stories/typography/NoWidow.stories.js +61 -0
  32. package/dist/theme/components/TextStyles.d.ts +5 -0
  33. package/dist/theme/components/TextStyles.js +8 -0
  34. package/package.json +6 -1
@@ -1,2 +1,2 @@
1
1
  import { PageTitleI } from '../../interfaces';
2
- export declare const PageTitle: ({ title }: PageTitleI) => import("react/jsx-runtime").JSX.Element;
2
+ export declare const PageTitle: ({ title, description }: PageTitleI) => import("react/jsx-runtime").JSX.Element;
@@ -1,5 +1,7 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Container, Text } from '@chakra-ui/react';
3
- export const PageTitle = ({ title }) => {
4
- return (_jsx(Container, { variant: "ghost", mt: 4, mb: 6, children: _jsx(Text, { variant: "title", textAlign: "center", px: [10, 0], noOfLines: [2, 1], children: title }) }));
3
+ export const PageTitle = ({ title, description }) => {
4
+ return (_jsxs(Container, { variant: "ghost", mt: 4, mb: 6, children: [_jsx(Text, { variant: "title", textAlign: "center", px: [10, 0], noOfLines: [2, 1], children: title }), description && (_jsx(Text, { variant: "info", color: "text.body", lineHeight: "20px", textAlign: "center", mt: "2",
5
+ // Centred text wraps into even lines (no widows).
6
+ sx: { textWrap: 'balance' }, children: description }))] }));
5
7
  };
@@ -0,0 +1,2 @@
1
+ import { MessagePageI } from '../../../interfaces';
2
+ export declare const MessagePage: ({ title, description, actionLabel, actionHref, }: MessagePageI) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Button, Container, Stack, Text } from '@chakra-ui/react';
3
+ import { Footer, BasePageWrapper, AuthHeader } from '../../../components';
4
+ // A page with only a message: a title, a line under it and, optionally, one link
5
+ // button (e.g. "Back to my texts" opening the texting app).
6
+ export const MessagePage = ({ title, description, actionLabel, actionHref, }) => {
7
+ const showAction = Boolean(actionLabel && actionHref);
8
+ return (_jsxs(BasePageWrapper, { flushMobile: true, children: [_jsxs(Container, { size: "onboarding", bg: "neutral.white", boxShadow: { base: 'none', md: 'lg' }, borderRadius: { base: 0, md: 'sm' }, maxW: { base: '100%', md: 'container.md' }, flex: { base: 1, md: 'initial' }, display: { base: 'flex', md: 'block' }, flexDirection: "column", children: [_jsx(AuthHeader, {}), _jsxs(Stack, { my: "6", children: [_jsx(Text, { variant: "title", textAlign: "center", px: "10", children: title }), _jsx(Text, { fontFamily: "secondary", textAlign: "center", px: ['10', '20'],
9
+ // Centred text wraps into even lines (no widows).
10
+ sx: { textWrap: 'balance' }, children: description })] }), showAction && (_jsx(Box, { px: ['container.sm', 'container.md', 'container.lg'], pb: "container.md", mb: "6", children: _jsx(Button, { as: "a", href: actionHref, w: "full", children: actionLabel }) }))] }), _jsx(Footer, {})] }));
11
+ };
@@ -1,2 +1,2 @@
1
1
  import { SignInI } from '../../../interfaces';
2
- export declare const SignIn: ({ isPartner, isLoading, handleForgotPassword, handleSignIn, handleSignUp, handleGoogleSignIn, handleAppleSignIn, loginErrorMessage, values, }: SignInI) => import("react/jsx-runtime").JSX.Element;
2
+ export declare const SignIn: ({ isPartner, isLoading, handleForgotPassword, handleSignIn, handleSignUp, handleGoogleSignIn, handleAppleSignIn, loginErrorMessage, heading, description, values, }: SignInI) => import("react/jsx-runtime").JSX.Element;
@@ -13,13 +13,13 @@ import { Box, Button, Center, Container, Divider, Flex, Stack, Text, } from '@ch
13
13
  import { Footer, PageTitle, CardFooter, PasswordInput, TextInput, ButtonLoader, BasePageWrapper, AuthHeader, GoogleIcon, AppleIcon, } from '../../../components';
14
14
  import { isEmptyField } from '../../../utils';
15
15
  import { useSignIn } from '../../../hooks';
16
- export const SignIn = ({ isPartner, isLoading, handleForgotPassword, handleSignIn, handleSignUp, handleGoogleSignIn, handleAppleSignIn, loginErrorMessage, values = { email: '', password: '' }, }) => {
16
+ export const SignIn = ({ isPartner, isLoading, handleForgotPassword, handleSignIn, handleSignUp, handleGoogleSignIn, handleAppleSignIn, loginErrorMessage, heading, description, values = { email: '', password: '' }, }) => {
17
17
  const { inputs, handleInputChange, isValidated, setIsValidated } = useSignIn(values);
18
18
  // Social sign-in renders only when a handler is provided (dark otherwise).
19
19
  const showGoogle = Boolean(handleGoogleSignIn);
20
20
  const showApple = Boolean(handleAppleSignIn);
21
21
  const showSocial = showGoogle || showApple;
22
- return (_jsxs(BasePageWrapper, { flushMobile: true, children: [_jsxs(Container, { size: "onboarding", bg: "neutral.white", boxShadow: { base: 'none', md: 'lg' }, borderRadius: { base: 0, md: 'sm' }, maxW: { base: '100%', md: 'container.md' }, flex: { base: 1, md: 'initial' }, display: { base: 'flex', md: 'block' }, flexDirection: "column", children: [_jsx(AuthHeader, { isPartner: isPartner }), _jsx(PageTitle, { title: showSocial ? t('signin.titleSocial') : t('signin.title') }), _jsx(Box, { px: ['container.sm', 'container.md', 'container.lg'], mb: 6, children: _jsxs(Stack, { spacing: "base", children: [showSocial && (_jsxs(Stack, { spacing: "base", children: [showGoogle && (_jsx(Button, { variant: "secondary", leftIcon: _jsx(GoogleIcon, { size: 20 }), onClick: handleGoogleSignIn, children: t('signup.continueGoogle') })), showApple && (_jsx(Button, { variant: "secondary", leftIcon: _jsx(AppleIcon, { size: 20 }), onClick: handleAppleSignIn, children: t('signup.continueApple') })), _jsxs(Flex, { align: "center", gap: "3", py: "0", children: [_jsx(Divider, { flex: "1", borderColor: "border.default" }), _jsx(Text, { variant: "caption", children: t('signup.or') }), _jsx(Divider, { flex: "1", borderColor: "border.default" })] })] })), _jsxs(Stack, { spacing: "base", as: "form", children: [Boolean(loginErrorMessage) && (_jsx(Text, { variant: "error", children: loginErrorMessage })), _jsx(TextInput, { showLabel: false, autoCapitalize: "none", autoCorrect: "off", autoComplete: "username", errorMessage: t('forms.email') + ' ' + t('forms.required'), hasError: isValidated && isEmptyField(inputs.email), id: "email", placeholder: t('forms.email'), value: values.email || inputs.email, handleChange: (event) => {
22
+ return (_jsxs(BasePageWrapper, { flushMobile: true, children: [_jsxs(Container, { size: "onboarding", bg: "neutral.white", boxShadow: { base: 'none', md: 'lg' }, borderRadius: { base: 0, md: 'sm' }, maxW: { base: '100%', md: 'container.md' }, flex: { base: 1, md: 'initial' }, display: { base: 'flex', md: 'block' }, flexDirection: "column", children: [_jsx(AuthHeader, { isPartner: isPartner }), _jsx(PageTitle, { title: heading !== null && heading !== void 0 ? heading : (showSocial ? t('signin.titleSocial') : t('signin.title')), description: description }), _jsx(Box, { px: ['container.sm', 'container.md', 'container.lg'], mb: 6, children: _jsxs(Stack, { spacing: "base", children: [showSocial && (_jsxs(Stack, { spacing: "base", children: [showGoogle && (_jsx(Button, { variant: "secondary", leftIcon: _jsx(GoogleIcon, { size: 20 }), onClick: handleGoogleSignIn, children: t('signup.continueGoogle') })), showApple && (_jsx(Button, { variant: "secondary", leftIcon: _jsx(AppleIcon, { size: 20 }), onClick: handleAppleSignIn, children: t('signup.continueApple') })), _jsxs(Flex, { align: "center", gap: "3", py: "0", children: [_jsx(Divider, { flex: "1", borderColor: "border.default" }), _jsx(Text, { variant: "caption", children: t('signup.or') }), _jsx(Divider, { flex: "1", borderColor: "border.default" })] })] })), _jsxs(Stack, { spacing: "base", as: "form", children: [Boolean(loginErrorMessage) && (_jsx(Text, { variant: "error", children: loginErrorMessage })), _jsx(TextInput, { showLabel: false, autoCapitalize: "none", autoCorrect: "off", autoComplete: "username", errorMessage: t('forms.email') + ' ' + t('forms.required'), hasError: isValidated && isEmptyField(inputs.email), id: "email", placeholder: t('forms.email'), value: values.email || inputs.email, handleChange: (event) => {
23
23
  values.email = event.target.value;
24
24
  handleInputChange(event);
25
25
  } }), _jsx(PasswordInput, { showLabel: false, autoComplete: "current-password", errorMessage: t('forms.password') + ' ' + t('forms.required'), hasError: isValidated && isEmptyField(inputs.password), id: "password", placeholder: t('forms.password'), value: values.password || inputs.password, handleChange: (event) => {
@@ -31,5 +31,5 @@ export const SignIn = ({ isPartner, isLoading, handleForgotPassword, handleSignI
31
31
  !isEmptyField(inputs.password)) {
32
32
  handleSignIn(inputs);
33
33
  }
34
- }), children: t('signin.signinBt') }), _jsx(Center, { children: _jsx(Button, { onClick: handleForgotPassword, variant: "text", children: t('signin.forgotBt') }) })] })] }) }), _jsx(CardFooter, { title: isPartner ? t('signin.signupPartner') : t('signin.signup'), buttonLabel: t('signin.signupBt'), handleButtonClick: handleSignUp })] }), _jsx(Footer, {})] }));
34
+ }), children: t('signin.signinBt') }), _jsx(Center, { children: _jsx(Button, { onClick: handleForgotPassword, variant: "text", children: t('signin.forgotBt') }) })] })] }) }), handleSignUp && (_jsx(CardFooter, { title: isPartner ? t('signin.signupPartner') : t('signin.signup'), buttonLabel: t('signin.signupBt'), handleButtonClick: handleSignUp }))] }), _jsx(Footer, {})] }));
35
35
  };
@@ -1,2 +1,2 @@
1
1
  import { TwoFactorI } from '../../../interfaces';
2
- export declare const TwoFactor: ({ onReset, onResend, isDialog, isLoading, isPartner, twoFactorMethod, }: TwoFactorI) => import("react/jsx-runtime").JSX.Element;
2
+ export declare const TwoFactor: ({ onReset, onResend, isDialog, isLoading, isPartner, twoFactorMethod, heading, description, warning, submitLabel, errorMessage, }: TwoFactorI) => import("react/jsx-runtime").JSX.Element;
@@ -1,10 +1,12 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { t } from 'i18next';
3
3
  import { Box, Button, Text, Container, Stack, Center } from '@chakra-ui/react';
4
4
  import { Footer, ButtonLoader, TextInput, BasePageWrapper, AuthHeader, } from '../../../components';
5
5
  import { isEmptyField } from '../../../utils';
6
6
  import { useTwoFactor } from '../../../hooks';
7
- export const TwoFactor = ({ onReset, onResend, isDialog, isLoading, isPartner, twoFactorMethod, }) => {
7
+ export const TwoFactor = ({ onReset, onResend, isDialog, isLoading, isPartner, twoFactorMethod, heading, description, warning, submitLabel, errorMessage, }) => {
8
8
  const { code, handleInputChange, handleSubmit, isValidated } = useTwoFactor(onReset);
9
- return (_jsxs(BasePageWrapper, { flushMobile: !isDialog, children: [_jsxs(Container, { size: "onboarding", bg: "neutral.white", boxShadow: isDialog ? 'none' : { base: 'none', md: 'lg' }, borderRadius: isDialog ? 'sm' : { base: 0, md: 'sm' }, maxW: isDialog ? 'container.md' : { base: '100%', md: 'container.md' }, flex: isDialog ? 'initial' : { base: 1, md: 'initial' }, display: isDialog ? 'block' : { base: 'flex', md: 'block' }, flexDirection: "column", children: [_jsx(AuthHeader, { isPartner: isPartner }), _jsxs(Stack, { my: "6", children: [_jsx(Text, { variant: "title", textAlign: "center", px: "10", children: t('reset.2FA.title') }), _jsxs(Text, { fontFamily: "secondary", textAlign: "center", px: ['10', '20'], children: [twoFactorMethod === 'email' && t('reset.2FA.subtitleEmail'), twoFactorMethod === 'sms' && t('reset.2FA.subtitleSms')] })] }), _jsx(Box, { px: ['container.sm', 'container.md', 'container.lg'], pb: "container.md", mb: "6", children: _jsxs(Stack, { spacing: "4", children: [_jsx(TextInput, { errorMessage: t('forms.code') + ' ' + t('forms.required'), hasError: isValidated && isEmptyField(code), id: "code", placeholder: t('reset.2FA.placeholder'), value: code, handleChange: handleInputChange }), _jsx(Button, { isLoading: isLoading, spinner: _jsx(ButtonLoader, {}), onClick: handleSubmit, children: t('forms.submit') }), onResend && (_jsx(Center, { children: _jsx(Button, { onClick: onResend, variant: "text", children: t('reset.2FA.resend') }) }))] }) })] }), !isDialog && _jsx(Footer, {})] }));
9
+ return (_jsxs(BasePageWrapper, { flushMobile: !isDialog, children: [_jsxs(Container, { size: "onboarding", bg: "neutral.white", boxShadow: isDialog ? 'none' : { base: 'none', md: 'lg' }, borderRadius: isDialog ? 'sm' : { base: 0, md: 'sm' }, maxW: isDialog ? 'container.md' : { base: '100%', md: 'container.md' }, flex: isDialog ? 'initial' : { base: 1, md: 'initial' }, display: isDialog ? 'block' : { base: 'flex', md: 'block' }, flexDirection: "column", children: [_jsx(AuthHeader, { isPartner: isPartner }), _jsxs(Stack, { my: "6", children: [_jsx(Text, { variant: "title", textAlign: "center", px: "10", children: heading !== null && heading !== void 0 ? heading : t('reset.2FA.title') }), _jsx(Text, { fontFamily: "secondary", textAlign: "center", px: ['10', '20'],
10
+ // Centred text wraps into even lines (no widows).
11
+ sx: { textWrap: 'balance' }, children: description !== null && description !== void 0 ? description : (_jsxs(_Fragment, { children: [twoFactorMethod === 'email' && t('reset.2FA.subtitleEmail'), twoFactorMethod === 'sms' && t('reset.2FA.subtitleSms')] })) }), warning && (_jsx(Text, { fontFamily: "secondary", fontWeight: "semibold", textAlign: "center", px: ['10', '20'], pt: "4", sx: { textWrap: 'balance' }, children: warning }))] }), _jsx(Box, { px: ['container.sm', 'container.md', 'container.lg'], pb: "container.md", mb: "6", children: _jsxs(Stack, { spacing: "4", children: [Boolean(errorMessage) && _jsx(Text, { variant: "error", children: errorMessage }), _jsx(TextInput, { errorMessage: t('forms.code') + ' ' + t('forms.required'), hasError: isValidated && isEmptyField(code), id: "code", placeholder: t('reset.2FA.placeholder'), value: code, handleChange: handleInputChange }), _jsx(Button, { isLoading: isLoading, spinner: _jsx(ButtonLoader, {}), onClick: handleSubmit, children: submitLabel !== null && submitLabel !== void 0 ? submitLabel : t('forms.submit') }), onResend && (_jsx(Center, { children: _jsx(Button, { onClick: onResend, variant: "text", children: t('reset.2FA.resend') }) }))] }) })] }), !isDialog && _jsx(Footer, {})] }));
10
12
  };
@@ -9,3 +9,4 @@ export * from './Welcome';
9
9
  export * from './NewPassword';
10
10
  export * from './ResendResetPassword';
11
11
  export * from './TwoFactor';
12
+ export * from './MessagePage';
@@ -9,3 +9,4 @@ export * from './Welcome';
9
9
  export * from './NewPassword';
10
10
  export * from './ResendResetPassword';
11
11
  export * from './TwoFactor';
12
+ export * from './MessagePage';
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { ActivateAccount, ActiveSubscription, AddCardBanner, AddEditContactPanel, AddFolder, AddHomeContent, AddHomeItem, AddHomeItemHeader, AddMedia, AddPopup, AddPropertyRecords, Address, AddTile, AddToHomefile, AlertBanner, AphwTile, AppBar, AppliancesReceived, AssignableReceipts, BackendAlert, BackHeader, BarDivider, BrandLogo, CalendarWidget, CancelAccount, CatalogPopup, CloudsAnimation, Close, ConfirmHomeDetails, ConfirmProperty, ConfirmPropertyBody, ConfirmPropertyFooter, ConfirmPropertyRecords, ContactList, ContactsContent, ContainerHeader, ControlledWizard, CreateDocumentHeader, CreateHouseholdItemHeader, CreditCardContainer, CreditCardError, CustomerTile, DeleteBanner, Dialog, DisplayFiles, DisplayFilesDetail, DisplayOptions, DisplayReceipts, DocumentMenu, DocumentNameHeader, DocumentPreview, DragDropArea, DynamicForm, EditAccountType, EditHomeBody, EditHomeFooter, EditHomeHeader, EditHomePanel, EditItemName, EmailPermissions, EmailValidation, Feedback, FileDetail, FilesUploader, FlowStep, FolderDetail, FolderDetailBody, FolderDetailContent, FolderDetailFooter, FolderDetailHeader, FolderInfo, FolderSharing, FolderTypeSelection, FooterButtons, FooterDrawer, GenericBackHeader, GroupCard, GroupsContainer, GroupsHeader, Header, HelpContent, HomeAssistant, HomeAssistantPanel, HomeAssistantTutorial, HomeAssistantWizardPanel, HomeAssistantWizardSteps, HomeBoardGrid, HomeBoardTour, HomeCard, HomeCardWithRecipent, HomeDetailsContent, HomefileMonitoring, HomeHeader, HomiPresence, HomiVoice, InlineEditField, ProgressBar, RevealValueCard, StaggerReveal, HomeItemList, HomeItemManualInfo, HomeMonitor, HomeMonitorPanel, HomeMonitorSteps, HomeSharedWith, HomieAddItems, HomiSms, InboxForwardBanner, InboxTile, IncludePublicRecords, ItemFormPanel, ItemFormTabs, ItemNameHeader, ItemsReviewBanner, ItemSubTypeSelect, Launchpad, LaunchpadApplianceAutofiler, LaunchpadAppliancePanel, LaunchpadAutofilerBanner, LaunchpadReceiptAutofiler, LaunchpadReceiptPanel, LaunchpadTour, LeftPanel, Loading, MediaDetailsStep, MessageChatPanel, MessagePanel, MinimizedTile, MinimizedTiles, MonthlyCharge, MortgageInfo, MortgageInfoReadOnly, MoveModal, MyHomeCard, MyHomes, MyProfileBody, MyProfileContent, MyProfileFooter, MyProfileHeader, MyProfilePanel, NewCreditCard, NewCreditCardHeader, NewHomeDetails, NewParterDetails, NewPassword, NotBeChargedBanner, NotificationCard, Notifications, NotificationsPanel, NotificationsReminder, NpsScore, Overlay, PanelHeader, PartnerActiveSubscription, PartnerCatalogPanel, PartnerContent, PartnerCustomerCard, PartnerCustomerCode, PartnerCustomersToolbar, PartnerCustomersWrapper, PartnerDetails, PartnerHomeHeader, PartnerImages, PartnerPanel, PartnerServiceCard, PartnerServiceTicketCard, PartnerServiceTicketsToolbar, PartnerServiceTicketsWrapper, PartnerSidebarMenu, PartnerWizardStepBrand, PartnerWizardStepServices, PartnerWizardStepServicesSelect, PartnerWizardStepServiceZones, PartnerWizardStepCta, PartnerWizardStepSocial, PartnerWizardStepHeader, PartnerWizardStepLayout, PartnerWizardSuccessHeader, PartnerWizardSuccessBody, PartnerWizardSteps, PasswordInput, PaymentBanner, PaymentReceipts, PdfButton, PeopleConnected, ProfileDetailsTab, ProfilePaymentTab, ProjectList, PropertyRecords, PropertyTaxes, ReadOnlyDynamicForm, ReadOnlyImage, ReadOnlyToggle, ReceiptAutofiler, ReceiptBody, ReceiptContent, ReceiptDetails, ReceiptFilters, ReceiptFooter, ReceiptHeader, ReceiptInfos, ReceiptItem, ReceiptItems, ReceiptPDF, ReceiptsDisplayOptions, ReceiptsFiled, ReceiptsHeader, ReceiptsInfo, ReceiptsReceivedContent, RecipientForm, RecipientsToShare, RecipientTab, RecordsInputs, ResendResetPassword, ResetPassword, ReviewBanner, RightPanel, RolePermissionsTab, RoomHeader, RoomsBoardTour, RoomsMenu, RoomsMenuMobile, RoomStep, RoomVideo, SalesTax, SatisfactionTile, SearchItemLoader, SearchRecords, SectionHeader, SendCommunication, ShareContactsContent, SharedAccounts, SharedHomeContent, SharedHomeHeader, ShareHome, ShareHomeConnections, ShareHomeForm, ShineTile, ShortPartnerTile, SignIn, SignUp, SkeletonBox, StartHomiSetup, StepHeader, StorageUsed, Subscription, SubscriptionCard, SubscriptionTable, Summary, TabsHeader, TextInput, ToBeDeletedBody, ToBeDeletedContent, ToBeDeletedFooter, ToBeDeletedHeader, TourButton, Trash, TrendingValue, TrialBanner, TutorialsButton, TwoFactor, TwoFactorDialog, TwoFactorSetting, UpdateList, UploadTile, UserDetails, ValueMonitor, VideoPlayer, VideoPlayerModal, ViewContactPanel, WeatherWidget, WidgetCarousel, WellDone, WizardBodyPadding, WizardStepGoal, WizardStepPrompt, WizardSuccessHeader, WizardTextHeader, WizardValueSummary, WizardValueSummaryBody, WizardValueSummaryFooter, YtdTile, } from './components';
1
+ export { ActivateAccount, ActiveSubscription, AddCardBanner, AddEditContactPanel, AddFolder, AddHomeContent, AddHomeItem, AddHomeItemHeader, AddMedia, AddPopup, AddPropertyRecords, Address, AddTile, AddToHomefile, AlertBanner, AphwTile, AppBar, AppliancesReceived, AssignableReceipts, BackendAlert, BackHeader, BarDivider, BrandLogo, CalendarWidget, CancelAccount, CatalogPopup, CloudsAnimation, Close, ConfirmHomeDetails, ConfirmProperty, ConfirmPropertyBody, ConfirmPropertyFooter, ConfirmPropertyRecords, ContactList, ContactsContent, ContainerHeader, ControlledWizard, CreateDocumentHeader, CreateHouseholdItemHeader, CreditCardContainer, CreditCardError, CustomerTile, DeleteBanner, Dialog, DisplayFiles, DisplayFilesDetail, DisplayOptions, DisplayReceipts, DocumentMenu, DocumentNameHeader, DocumentPreview, DragDropArea, DynamicForm, EditAccountType, EditHomeBody, EditHomeFooter, EditHomeHeader, EditHomePanel, EditItemName, EmailPermissions, EmailValidation, Feedback, FileDetail, FilesUploader, FlowStep, FolderDetail, FolderDetailBody, FolderDetailContent, FolderDetailFooter, FolderDetailHeader, FolderInfo, FolderSharing, FolderTypeSelection, FooterButtons, FooterDrawer, GenericBackHeader, GroupCard, GroupsContainer, GroupsHeader, Header, HelpContent, HomeAssistant, HomeAssistantPanel, HomeAssistantTutorial, HomeAssistantWizardPanel, HomeAssistantWizardSteps, HomeBoardGrid, HomeBoardTour, HomeCard, HomeCardWithRecipent, HomeDetailsContent, HomefileMonitoring, HomeHeader, HomiPresence, HomiVoice, InlineEditField, ProgressBar, RevealValueCard, StaggerReveal, HomeItemList, HomeItemManualInfo, HomeMonitor, HomeMonitorPanel, HomeMonitorSteps, HomeSharedWith, HomieAddItems, HomiSms, InboxForwardBanner, InboxTile, IncludePublicRecords, ItemFormPanel, ItemFormTabs, ItemNameHeader, ItemsReviewBanner, ItemSubTypeSelect, Launchpad, LaunchpadApplianceAutofiler, LaunchpadAppliancePanel, LaunchpadAutofilerBanner, LaunchpadReceiptAutofiler, LaunchpadReceiptPanel, LaunchpadTour, LeftPanel, Loading, MediaDetailsStep, MessageChatPanel, MessagePage, MessagePanel, MinimizedTile, MinimizedTiles, MonthlyCharge, MortgageInfo, MortgageInfoReadOnly, MoveModal, MyHomeCard, MyHomes, MyProfileBody, MyProfileContent, MyProfileFooter, MyProfileHeader, MyProfilePanel, NewCreditCard, NewCreditCardHeader, NewHomeDetails, NewParterDetails, NewPassword, NotBeChargedBanner, NotificationCard, Notifications, NotificationsPanel, NotificationsReminder, NpsScore, Overlay, PanelHeader, PartnerActiveSubscription, PartnerCatalogPanel, PartnerContent, PartnerCustomerCard, PartnerCustomerCode, PartnerCustomersToolbar, PartnerCustomersWrapper, PartnerDetails, PartnerHomeHeader, PartnerImages, PartnerPanel, PartnerServiceCard, PartnerServiceTicketCard, PartnerServiceTicketsToolbar, PartnerServiceTicketsWrapper, PartnerSidebarMenu, PartnerWizardStepBrand, PartnerWizardStepServices, PartnerWizardStepServicesSelect, PartnerWizardStepServiceZones, PartnerWizardStepCta, PartnerWizardStepSocial, PartnerWizardStepHeader, PartnerWizardStepLayout, PartnerWizardSuccessHeader, PartnerWizardSuccessBody, PartnerWizardSteps, PasswordInput, PaymentBanner, PaymentReceipts, PdfButton, PeopleConnected, ProfileDetailsTab, ProfilePaymentTab, ProjectList, PropertyRecords, PropertyTaxes, ReadOnlyDynamicForm, ReadOnlyImage, ReadOnlyToggle, ReceiptAutofiler, ReceiptBody, ReceiptContent, ReceiptDetails, ReceiptFilters, ReceiptFooter, ReceiptHeader, ReceiptInfos, ReceiptItem, ReceiptItems, ReceiptPDF, ReceiptsDisplayOptions, ReceiptsFiled, ReceiptsHeader, ReceiptsInfo, ReceiptsReceivedContent, RecipientForm, RecipientsToShare, RecipientTab, RecordsInputs, ResendResetPassword, ResetPassword, ReviewBanner, RightPanel, RolePermissionsTab, RoomHeader, RoomsBoardTour, RoomsMenu, RoomsMenuMobile, RoomStep, RoomVideo, SalesTax, SatisfactionTile, SearchItemLoader, SearchRecords, SectionHeader, SendCommunication, ShareContactsContent, SharedAccounts, SharedHomeContent, SharedHomeHeader, ShareHome, ShareHomeConnections, ShareHomeForm, ShineTile, ShortPartnerTile, SignIn, SignUp, SkeletonBox, StartHomiSetup, StepHeader, StorageUsed, Subscription, SubscriptionCard, SubscriptionTable, Summary, TabsHeader, TextInput, ToBeDeletedBody, ToBeDeletedContent, ToBeDeletedFooter, ToBeDeletedHeader, TourButton, Trash, TrendingValue, TrialBanner, TutorialsButton, TwoFactor, TwoFactorDialog, TwoFactorSetting, UpdateList, UploadTile, UserDetails, ValueMonitor, VideoPlayer, VideoPlayerModal, ViewContactPanel, WeatherWidget, WidgetCarousel, WellDone, WizardBodyPadding, WizardStepGoal, WizardStepPrompt, WizardSuccessHeader, WizardTextHeader, WizardValueSummary, WizardValueSummaryBody, WizardValueSummaryFooter, YtdTile, } from './components';
2
2
  export { useCustomToast, useConfirmPropertyModel, usePrefersReducedMotion, useCountUp, useDirtySave, } from './hooks';
3
3
  export { useFilesUploader } from './hooks/folderPanel/useFilesUploader';
4
4
  export { randomColor, mapApiObjectToFormFields, mapForecastToWidget, } from './utils';
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { ActivateAccount, ActiveSubscription, AddCardBanner, AddEditContactPanel, AddFolder, AddHomeContent, AddHomeItem, AddHomeItemHeader, AddMedia, AddPopup, AddPropertyRecords, Address, AddTile, AddToHomefile, AlertBanner, AphwTile, AppBar, AppliancesReceived, AssignableReceipts, BackendAlert, BackHeader, BarDivider, BrandLogo, CalendarWidget, CancelAccount, CatalogPopup, CloudsAnimation, Close, ConfirmHomeDetails, ConfirmProperty, ConfirmPropertyBody, ConfirmPropertyFooter, ConfirmPropertyRecords, ContactList, ContactsContent, ContainerHeader, ControlledWizard, CreateDocumentHeader, CreateHouseholdItemHeader, CreditCardContainer, CreditCardError, CustomerTile, DeleteBanner, Dialog, DisplayFiles, DisplayFilesDetail, DisplayOptions, DisplayReceipts, DocumentMenu, DocumentNameHeader, DocumentPreview, DragDropArea, DynamicForm, EditAccountType, EditHomeBody, EditHomeFooter, EditHomeHeader, EditHomePanel, EditItemName, EmailPermissions, EmailValidation, Feedback, FileDetail, FilesUploader, FlowStep, FolderDetail, FolderDetailBody, FolderDetailContent, FolderDetailFooter, FolderDetailHeader, FolderInfo, FolderSharing, FolderTypeSelection, FooterButtons, FooterDrawer, GenericBackHeader, GroupCard, GroupsContainer, GroupsHeader, Header, HelpContent, HomeAssistant, HomeAssistantPanel, HomeAssistantTutorial, HomeAssistantWizardPanel, HomeAssistantWizardSteps, HomeBoardGrid, HomeBoardTour, HomeCard, HomeCardWithRecipent, HomeDetailsContent, HomefileMonitoring, HomeHeader, HomiPresence, HomiVoice, InlineEditField, ProgressBar, RevealValueCard, StaggerReveal, HomeItemList, HomeItemManualInfo, HomeMonitor, HomeMonitorPanel, HomeMonitorSteps, HomeSharedWith, HomieAddItems, HomiSms, InboxForwardBanner, InboxTile, IncludePublicRecords, ItemFormPanel, ItemFormTabs, ItemNameHeader, ItemsReviewBanner, ItemSubTypeSelect, Launchpad, LaunchpadApplianceAutofiler, LaunchpadAppliancePanel, LaunchpadAutofilerBanner, LaunchpadReceiptAutofiler, LaunchpadReceiptPanel, LaunchpadTour, LeftPanel, Loading, MediaDetailsStep, MessageChatPanel, MessagePanel, MinimizedTile, MinimizedTiles, MonthlyCharge, MortgageInfo, MortgageInfoReadOnly, MoveModal, MyHomeCard, MyHomes, MyProfileBody, MyProfileContent, MyProfileFooter, MyProfileHeader, MyProfilePanel, NewCreditCard, NewCreditCardHeader, NewHomeDetails, NewParterDetails, NewPassword, NotBeChargedBanner, NotificationCard, Notifications, NotificationsPanel, NotificationsReminder, NpsScore, Overlay, PanelHeader, PartnerActiveSubscription, PartnerCatalogPanel, PartnerContent, PartnerCustomerCard, PartnerCustomerCode, PartnerCustomersToolbar, PartnerCustomersWrapper, PartnerDetails, PartnerHomeHeader, PartnerImages, PartnerPanel, PartnerServiceCard, PartnerServiceTicketCard, PartnerServiceTicketsToolbar, PartnerServiceTicketsWrapper, PartnerSidebarMenu, PartnerWizardStepBrand, PartnerWizardStepServices, PartnerWizardStepServicesSelect, PartnerWizardStepServiceZones, PartnerWizardStepCta, PartnerWizardStepSocial, PartnerWizardStepHeader, PartnerWizardStepLayout, PartnerWizardSuccessHeader, PartnerWizardSuccessBody, PartnerWizardSteps, PasswordInput, PaymentBanner, PaymentReceipts, PdfButton, PeopleConnected, ProfileDetailsTab, ProfilePaymentTab, ProjectList, PropertyRecords, PropertyTaxes, ReadOnlyDynamicForm, ReadOnlyImage, ReadOnlyToggle, ReceiptAutofiler, ReceiptBody, ReceiptContent, ReceiptDetails, ReceiptFilters, ReceiptFooter, ReceiptHeader, ReceiptInfos, ReceiptItem, ReceiptItems, ReceiptPDF, ReceiptsDisplayOptions, ReceiptsFiled, ReceiptsHeader, ReceiptsInfo, ReceiptsReceivedContent, RecipientForm, RecipientsToShare, RecipientTab, RecordsInputs, ResendResetPassword, ResetPassword, ReviewBanner, RightPanel, RolePermissionsTab, RoomHeader, RoomsBoardTour, RoomsMenu, RoomsMenuMobile, RoomStep, RoomVideo, SalesTax, SatisfactionTile, SearchItemLoader, SearchRecords, SectionHeader, SendCommunication, ShareContactsContent, SharedAccounts, SharedHomeContent, SharedHomeHeader, ShareHome, ShareHomeConnections, ShareHomeForm, ShineTile, ShortPartnerTile, SignIn, SignUp, SkeletonBox, StartHomiSetup, StepHeader, StorageUsed, Subscription, SubscriptionCard, SubscriptionTable, Summary, TabsHeader, TextInput, ToBeDeletedBody, ToBeDeletedContent, ToBeDeletedFooter, ToBeDeletedHeader, TourButton, Trash, TrendingValue, TrialBanner, TutorialsButton, TwoFactor, TwoFactorDialog, TwoFactorSetting, UpdateList, UploadTile, UserDetails, ValueMonitor, VideoPlayer, VideoPlayerModal, ViewContactPanel, WeatherWidget, WidgetCarousel, WellDone, WizardBodyPadding, WizardStepGoal, WizardStepPrompt, WizardSuccessHeader, WizardTextHeader, WizardValueSummary, WizardValueSummaryBody, WizardValueSummaryFooter, YtdTile, } from './components';
1
+ export { ActivateAccount, ActiveSubscription, AddCardBanner, AddEditContactPanel, AddFolder, AddHomeContent, AddHomeItem, AddHomeItemHeader, AddMedia, AddPopup, AddPropertyRecords, Address, AddTile, AddToHomefile, AlertBanner, AphwTile, AppBar, AppliancesReceived, AssignableReceipts, BackendAlert, BackHeader, BarDivider, BrandLogo, CalendarWidget, CancelAccount, CatalogPopup, CloudsAnimation, Close, ConfirmHomeDetails, ConfirmProperty, ConfirmPropertyBody, ConfirmPropertyFooter, ConfirmPropertyRecords, ContactList, ContactsContent, ContainerHeader, ControlledWizard, CreateDocumentHeader, CreateHouseholdItemHeader, CreditCardContainer, CreditCardError, CustomerTile, DeleteBanner, Dialog, DisplayFiles, DisplayFilesDetail, DisplayOptions, DisplayReceipts, DocumentMenu, DocumentNameHeader, DocumentPreview, DragDropArea, DynamicForm, EditAccountType, EditHomeBody, EditHomeFooter, EditHomeHeader, EditHomePanel, EditItemName, EmailPermissions, EmailValidation, Feedback, FileDetail, FilesUploader, FlowStep, FolderDetail, FolderDetailBody, FolderDetailContent, FolderDetailFooter, FolderDetailHeader, FolderInfo, FolderSharing, FolderTypeSelection, FooterButtons, FooterDrawer, GenericBackHeader, GroupCard, GroupsContainer, GroupsHeader, Header, HelpContent, HomeAssistant, HomeAssistantPanel, HomeAssistantTutorial, HomeAssistantWizardPanel, HomeAssistantWizardSteps, HomeBoardGrid, HomeBoardTour, HomeCard, HomeCardWithRecipent, HomeDetailsContent, HomefileMonitoring, HomeHeader, HomiPresence, HomiVoice, InlineEditField, ProgressBar, RevealValueCard, StaggerReveal, HomeItemList, HomeItemManualInfo, HomeMonitor, HomeMonitorPanel, HomeMonitorSteps, HomeSharedWith, HomieAddItems, HomiSms, InboxForwardBanner, InboxTile, IncludePublicRecords, ItemFormPanel, ItemFormTabs, ItemNameHeader, ItemsReviewBanner, ItemSubTypeSelect, Launchpad, LaunchpadApplianceAutofiler, LaunchpadAppliancePanel, LaunchpadAutofilerBanner, LaunchpadReceiptAutofiler, LaunchpadReceiptPanel, LaunchpadTour, LeftPanel, Loading, MediaDetailsStep, MessageChatPanel, MessagePage, MessagePanel, MinimizedTile, MinimizedTiles, MonthlyCharge, MortgageInfo, MortgageInfoReadOnly, MoveModal, MyHomeCard, MyHomes, MyProfileBody, MyProfileContent, MyProfileFooter, MyProfileHeader, MyProfilePanel, NewCreditCard, NewCreditCardHeader, NewHomeDetails, NewParterDetails, NewPassword, NotBeChargedBanner, NotificationCard, Notifications, NotificationsPanel, NotificationsReminder, NpsScore, Overlay, PanelHeader, PartnerActiveSubscription, PartnerCatalogPanel, PartnerContent, PartnerCustomerCard, PartnerCustomerCode, PartnerCustomersToolbar, PartnerCustomersWrapper, PartnerDetails, PartnerHomeHeader, PartnerImages, PartnerPanel, PartnerServiceCard, PartnerServiceTicketCard, PartnerServiceTicketsToolbar, PartnerServiceTicketsWrapper, PartnerSidebarMenu, PartnerWizardStepBrand, PartnerWizardStepServices, PartnerWizardStepServicesSelect, PartnerWizardStepServiceZones, PartnerWizardStepCta, PartnerWizardStepSocial, PartnerWizardStepHeader, PartnerWizardStepLayout, PartnerWizardSuccessHeader, PartnerWizardSuccessBody, PartnerWizardSteps, PasswordInput, PaymentBanner, PaymentReceipts, PdfButton, PeopleConnected, ProfileDetailsTab, ProfilePaymentTab, ProjectList, PropertyRecords, PropertyTaxes, ReadOnlyDynamicForm, ReadOnlyImage, ReadOnlyToggle, ReceiptAutofiler, ReceiptBody, ReceiptContent, ReceiptDetails, ReceiptFilters, ReceiptFooter, ReceiptHeader, ReceiptInfos, ReceiptItem, ReceiptItems, ReceiptPDF, ReceiptsDisplayOptions, ReceiptsFiled, ReceiptsHeader, ReceiptsInfo, ReceiptsReceivedContent, RecipientForm, RecipientsToShare, RecipientTab, RecordsInputs, ResendResetPassword, ResetPassword, ReviewBanner, RightPanel, RolePermissionsTab, RoomHeader, RoomsBoardTour, RoomsMenu, RoomsMenuMobile, RoomStep, RoomVideo, SalesTax, SatisfactionTile, SearchItemLoader, SearchRecords, SectionHeader, SendCommunication, ShareContactsContent, SharedAccounts, SharedHomeContent, SharedHomeHeader, ShareHome, ShareHomeConnections, ShareHomeForm, ShineTile, ShortPartnerTile, SignIn, SignUp, SkeletonBox, StartHomiSetup, StepHeader, StorageUsed, Subscription, SubscriptionCard, SubscriptionTable, Summary, TabsHeader, TextInput, ToBeDeletedBody, ToBeDeletedContent, ToBeDeletedFooter, ToBeDeletedHeader, TourButton, Trash, TrendingValue, TrialBanner, TutorialsButton, TwoFactor, TwoFactorDialog, TwoFactorSetting, UpdateList, UploadTile, UserDetails, ValueMonitor, VideoPlayer, VideoPlayerModal, ViewContactPanel, WeatherWidget, WidgetCarousel, WellDone, WizardBodyPadding, WizardStepGoal, WizardStepPrompt, WizardSuccessHeader, WizardTextHeader, WizardValueSummary, WizardValueSummaryBody, WizardValueSummaryFooter, YtdTile, } from './components';
2
2
  export { useCustomToast, useConfirmPropertyModel, usePrefersReducedMotion, useCountUp, useDirtySave, } from './hooks';
3
3
  export { useFilesUploader } from './hooks/folderPanel/useFilesUploader';
4
4
  export { randomColor, mapApiObjectToFormFields, mapForecastToWidget, } from './utils';
@@ -1,3 +1,4 @@
1
1
  export interface PageTitleI {
2
2
  title: string;
3
+ description?: string;
3
4
  }
@@ -0,0 +1,6 @@
1
+ export interface MessagePageI {
2
+ title: string;
3
+ description: string;
4
+ actionLabel?: string;
5
+ actionHref?: string;
6
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -7,9 +7,11 @@ export interface SignInI {
7
7
  isPartner?: boolean;
8
8
  handleForgotPassword: () => void;
9
9
  handleSignIn: (form: SignInFormI) => void;
10
- handleSignUp: () => void;
10
+ handleSignUp?: () => void;
11
11
  handleGoogleSignIn?: () => void;
12
12
  handleAppleSignIn?: () => void;
13
13
  loginErrorMessage?: string;
14
+ heading?: string;
15
+ description?: string;
14
16
  values?: SignInFormI;
15
17
  }
@@ -5,4 +5,9 @@ export interface TwoFactorI {
5
5
  isLoading: boolean;
6
6
  isPartner?: boolean;
7
7
  twoFactorMethod: 'sms' | 'email' | '';
8
+ heading?: string;
9
+ description?: string;
10
+ warning?: string;
11
+ submitLabel?: string;
12
+ errorMessage?: string;
8
13
  }
@@ -5,4 +5,5 @@ export * from './ResetPassword.interface';
5
5
  export * from './SignIn.interface';
6
6
  export * from './SignUp.interface';
7
7
  export * from './TwoFactor.interface';
8
+ export * from './MessagePage.interface';
8
9
  export * from './Welcome.interface';
@@ -5,4 +5,5 @@ export * from './ResetPassword.interface';
5
5
  export * from './SignIn.interface';
6
6
  export * from './SignUp.interface';
7
7
  export * from './TwoFactor.interface';
8
+ export * from './MessagePage.interface';
8
9
  export * from './Welcome.interface';
@@ -1,4 +1,4 @@
1
- import { Meta } from '@storybook/react';
1
+ import { Meta, StoryObj } from '@storybook/react';
2
2
  import { PageTitleI } from '../../interfaces';
3
3
  declare const _default: Meta<PageTitleI>;
4
4
  export default _default;
@@ -8,3 +8,4 @@ export declare const PageTitleComponent: {
8
8
  title: string;
9
9
  };
10
10
  };
11
+ export declare const PageTitleWithDescription: StoryObj<PageTitleI>;
@@ -1,4 +1,14 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
1
10
  import { jsx as _jsx } from "react/jsx-runtime";
11
+ import { expect, within } from '@storybook/test';
2
12
  import { PageTitle } from '../../components';
3
13
  export default {
4
14
  title: 'Components/Onboarding',
@@ -10,3 +20,24 @@ export const PageTitleComponent = (args) => {
10
20
  PageTitleComponent.args = {
11
21
  title: "Page's title",
12
22
  };
23
+ const DESCRIPTION = 'Log in to your Homefile account to connect this phone. Then Homi will know it’s you.';
24
+ export const PageTitleWithDescription = {
25
+ args: {
26
+ title: 'Connect (512) 555-0147',
27
+ description: DESCRIPTION,
28
+ },
29
+ play: (_a) => __awaiter(void 0, [_a], void 0, function* ({ canvasElement }) {
30
+ const canvas = within(canvasElement);
31
+ const title = canvas.getByText('Connect (512) 555-0147');
32
+ const description = canvas.getByText(DESCRIPTION);
33
+ // The line sits under the title, centred like it, and is never cut off.
34
+ yield expect(description.getBoundingClientRect().top).toBeGreaterThanOrEqual(title.getBoundingClientRect().bottom);
35
+ yield expect(getComputedStyle(description).textAlign).toBe('center');
36
+ const descriptionStyle = getComputedStyle(description);
37
+ yield expect(descriptionStyle.getPropertyValue('-webkit-line-clamp')).toBe('none');
38
+ yield expect(descriptionStyle.overflow).toBe('visible');
39
+ // Centred text wraps into even lines; line height at most 4px over the font size.
40
+ yield expect(descriptionStyle.getPropertyValue('text-wrap-style')).toBe('balance');
41
+ yield expect(parseFloat(descriptionStyle.lineHeight) - parseFloat(descriptionStyle.fontSize)).toBeLessThanOrEqual(4);
42
+ }),
43
+ };
@@ -0,0 +1,9 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+ import { MessagePageI } from '../../../interfaces';
3
+ declare const _default: Meta<MessagePageI>;
4
+ export default _default;
5
+ export declare const Connected: StoryObj<MessagePageI>;
6
+ export declare const Expired: StoryObj<MessagePageI>;
7
+ export declare const TooManyTries: StoryObj<MessagePageI>;
8
+ export declare const ConnectedElsewhere: StoryObj<MessagePageI>;
9
+ export declare const Support: StoryObj<MessagePageI>;
@@ -0,0 +1,65 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { expect, within } from '@storybook/test';
11
+ import { MessagePage } from '../../../components';
12
+ export default {
13
+ title: 'Pages/Onboarding/Message Page',
14
+ component: MessagePage,
15
+ // A whole page: render edge to edge, as on a phone.
16
+ parameters: { layout: 'fullscreen' },
17
+ };
18
+ const BACK_TO_TEXTS = { actionLabel: 'Back to my texts', actionHref: 'sms:' };
19
+ // Every message page shows its whole title and line; the button only when it has
20
+ // both a label and a link, and then it opens the texting app.
21
+ const play = (_a) => __awaiter(void 0, [_a], void 0, function* ({ canvasElement, args }) {
22
+ var _b;
23
+ const canvas = within(canvasElement);
24
+ const title = canvas.getByText(args.title);
25
+ yield expect(title).toBeVisible();
26
+ yield expect(getComputedStyle(title).getPropertyValue('-webkit-line-clamp')).toBe('none');
27
+ yield expect(getComputedStyle(title).overflow).toBe('visible');
28
+ yield expect(canvas.getByText(args.description)).toBeVisible();
29
+ const links = canvas.queryAllByRole('link');
30
+ if (args.actionLabel && args.actionHref) {
31
+ yield expect(links).toHaveLength(1);
32
+ yield expect(links[0]).toHaveTextContent(args.actionLabel);
33
+ yield expect((_b = links[0].getAttribute('href')) === null || _b === void 0 ? void 0 : _b.startsWith('sms:')).toBe(true);
34
+ }
35
+ else {
36
+ yield expect(links).toHaveLength(0);
37
+ yield expect(canvas.queryAllByRole('button')).toHaveLength(0);
38
+ }
39
+ });
40
+ export const Connected = {
41
+ args: Object.assign({ title: '(512) 555-0147 is connected', description: 'Go back to your texts. Homi will know it’s you now.' }, BACK_TO_TEXTS),
42
+ play,
43
+ };
44
+ export const Expired = {
45
+ args: Object.assign({ title: 'This link has expired', description: 'Text Homi and ask for a new one.' }, BACK_TO_TEXTS),
46
+ play,
47
+ };
48
+ export const TooManyTries = {
49
+ args: Object.assign({ title: 'Too many tries', description: 'Text Homi for a new link later.' }, BACK_TO_TEXTS),
50
+ play,
51
+ };
52
+ export const ConnectedElsewhere = {
53
+ args: {
54
+ title: 'This phone is already connected to a different Homefile account',
55
+ description: 'If that’s wrong, contact support@homefile.com.',
56
+ },
57
+ play,
58
+ };
59
+ export const Support = {
60
+ args: {
61
+ title: 'We can’t connect this phone here',
62
+ description: 'Contact support@homefile.com and we’ll help.',
63
+ },
64
+ play,
65
+ };
@@ -1,4 +1,4 @@
1
- import { Meta } from '@storybook/react';
1
+ import { Meta, StoryObj } from '@storybook/react';
2
2
  import { SignInI } from '../../../interfaces';
3
3
  declare const _default: Meta;
4
4
  export default _default;
@@ -10,5 +10,12 @@ export declare const SignInPage: {
10
10
  email: string;
11
11
  password: string;
12
12
  };
13
+ handleForgotPassword: import("@vitest/spy").Mock<(...args: any[]) => any>;
14
+ handleSignIn: import("@vitest/spy").Mock<(...args: any[]) => any>;
15
+ handleSignUp: import("@vitest/spy").Mock<(...args: any[]) => any>;
13
16
  };
17
+ play({ canvasElement }: {
18
+ canvasElement: HTMLElement;
19
+ }): Promise<void>;
14
20
  };
21
+ export declare const SignInConnect: StoryObj<SignInI>;
@@ -1,4 +1,14 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
1
10
  import { jsx as _jsx } from "react/jsx-runtime";
11
+ import { expect, fn, within } from '@storybook/test';
2
12
  import { SignIn, CloudsAnimation } from '../../../components';
3
13
  export default {
4
14
  title: 'Pages/Onboarding',
@@ -13,4 +23,38 @@ export const SignInPage = (args) => {
13
23
  SignInPage.args = {
14
24
  isLoading: false,
15
25
  values: { email: 'damaprodutora@gmail.com', password: '123456' },
26
+ handleForgotPassword: fn(),
27
+ handleSignIn: fn(),
28
+ handleSignUp: fn(),
29
+ };
30
+ SignInPage.play = (_a) => __awaiter(void 0, [_a], void 0, function* ({ canvasElement }) {
31
+ const canvas = within(canvasElement);
32
+ // The default page is unchanged: its own title and the Sign-up box.
33
+ yield expect(canvas.getByText('Please Log in')).toBeVisible();
34
+ yield expect(canvas.getByText('Don’t have a Homefile account?')).toBeVisible();
35
+ yield expect(canvas.getByRole('button', { name: 'Sign up' })).toBeVisible();
36
+ });
37
+ const CONNECT_HEADING = 'Connect (512) 555-0147';
38
+ const CONNECT_LINE = 'Log in to your Homefile account to connect this phone. Then Homi will know it’s you.';
39
+ // The page a texter reaches from Homi's link to connect their phone.
40
+ export const SignInConnect = {
41
+ args: {
42
+ isLoading: false,
43
+ heading: CONNECT_HEADING,
44
+ description: CONNECT_LINE,
45
+ handleForgotPassword: fn(),
46
+ handleSignIn: fn(),
47
+ handleGoogleSignIn: fn(),
48
+ },
49
+ play: (_a) => __awaiter(void 0, [_a], void 0, function* ({ canvasElement }) {
50
+ const canvas = within(canvasElement);
51
+ yield expect(canvas.getByText(CONNECT_HEADING)).toBeVisible();
52
+ yield expect(canvas.getByText(CONNECT_LINE)).toBeVisible();
53
+ yield expect(canvas.getByRole('button', { name: 'Continue with Google' })).toBeVisible();
54
+ yield expect(canvas.getByRole('button', { name: 'Log in' })).toBeVisible();
55
+ // No way to make a new account here, and no Apple button.
56
+ yield expect(canvas.queryByText('Don’t have a Homefile account?')).toBeNull();
57
+ yield expect(canvas.queryByRole('button', { name: 'Sign up' })).toBeNull();
58
+ yield expect(canvas.queryByRole('button', { name: 'Continue with Apple' })).toBeNull();
59
+ }),
16
60
  };
@@ -1,5 +1,12 @@
1
- import { Meta } from '@storybook/react';
1
+ import { Meta, StoryObj } from '@storybook/react';
2
2
  import { TwoFactorI } from '../../../interfaces';
3
3
  declare const _default: Meta<TwoFactorI>;
4
4
  export default _default;
5
- export declare const TwoFactorPage: (args: TwoFactorI) => import("react/jsx-runtime").JSX.Element;
5
+ export declare const TwoFactorPage: {
6
+ (args: TwoFactorI): import("react/jsx-runtime").JSX.Element;
7
+ play({ canvasElement }: {
8
+ canvasElement: HTMLElement;
9
+ }): Promise<void>;
10
+ };
11
+ export declare const TwoFactorConnect: StoryObj<TwoFactorI>;
12
+ export declare const TwoFactorConnectWrongCode: StoryObj<TwoFactorI>;
@@ -1,5 +1,15 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
1
10
  import { jsx as _jsx } from "react/jsx-runtime";
2
11
  import { Box } from '@chakra-ui/react';
12
+ import { expect, fn, within } from '@storybook/test';
3
13
  import { TwoFactor } from '../../../components';
4
14
  export default {
5
15
  title: 'Pages/Onboarding',
@@ -19,3 +29,51 @@ export default {
19
29
  export const TwoFactorPage = (args) => {
20
30
  return _jsx(TwoFactor, Object.assign({}, args));
21
31
  };
32
+ TwoFactorPage.play = (_a) => __awaiter(void 0, [_a], void 0, function* ({ canvasElement }) {
33
+ const canvas = within(canvasElement);
34
+ // The default page is unchanged: its own title, subtitle and button.
35
+ yield expect(canvas.getByText('Please validate your identity to log in.')).toBeVisible();
36
+ yield expect(canvas.getByText('We’ve sent a 6-digit security code to your phone. Please enter that code below.')).toBeVisible();
37
+ yield expect(canvas.getByRole('button', { name: 'Submit' })).toBeVisible();
38
+ });
39
+ const CONNECT_HEADING = 'Check your email';
40
+ const CONNECT_LINE = 'We sent a 6-digit code to j•••••@example.com. Enter it to connect (512) 555-0147.';
41
+ const CONNECT_WARNING = 'If someone sent you this link and asked you to log in, don’t enter the code.';
42
+ const WRONG_CODE = 'That code didn’t work. Check the email and try again.';
43
+ const connectArgs = {
44
+ twoFactorMethod: 'email',
45
+ heading: CONNECT_HEADING,
46
+ description: CONNECT_LINE,
47
+ warning: CONNECT_WARNING,
48
+ submitLabel: 'Connect phone',
49
+ onReset: fn(),
50
+ onResend: fn(),
51
+ };
52
+ // The code step of the page a texter reaches from Homi's link.
53
+ export const TwoFactorConnect = {
54
+ args: connectArgs,
55
+ play: (_a) => __awaiter(void 0, [_a], void 0, function* ({ canvasElement }) {
56
+ const canvas = within(canvasElement);
57
+ yield expect(canvas.getByText(CONNECT_HEADING)).toBeVisible();
58
+ yield expect(canvas.getByText(CONNECT_LINE)).toBeVisible();
59
+ const warning = canvas.getByText(CONNECT_WARNING);
60
+ yield expect(warning).toBeVisible();
61
+ yield expect(Number(getComputedStyle(warning).fontWeight)).toBeGreaterThanOrEqual(600);
62
+ yield expect(canvas.getByRole('button', { name: 'Connect phone' })).toBeVisible();
63
+ yield expect(canvas.getByRole('button', { name: 'Resend new code' })).toBeVisible();
64
+ yield expect(canvas.queryByText('Please validate your identity to log in.')).toBeNull();
65
+ yield expect(canvas.queryByRole('button', { name: 'Submit' })).toBeNull();
66
+ }),
67
+ };
68
+ // After a wrong code: the error sits above the code field, and the warning stays.
69
+ export const TwoFactorConnectWrongCode = {
70
+ args: Object.assign(Object.assign({}, connectArgs), { errorMessage: WRONG_CODE }),
71
+ play: (_a) => __awaiter(void 0, [_a], void 0, function* ({ canvasElement }) {
72
+ const canvas = within(canvasElement);
73
+ const error = canvas.getByText(WRONG_CODE);
74
+ yield expect(error).toBeVisible();
75
+ yield expect(canvas.getByText(CONNECT_WARNING)).toBeVisible();
76
+ const codeField = canvas.getByPlaceholderText('Enter your code here');
77
+ yield expect(error.getBoundingClientRect().bottom).toBeLessThanOrEqual(codeField.getBoundingClientRect().top);
78
+ }),
79
+ };
@@ -1,5 +1,5 @@
1
1
  import { Meta } from '@storybook/react';
2
2
  import { InboxCardI } from '../../../interfaces';
3
- declare const _default: Meta;
3
+ declare const _default: Meta<InboxCardI>;
4
4
  export default _default;
5
5
  export declare const InboxCardComponent: (args: InboxCardI) => import("react/jsx-runtime").JSX.Element;
@@ -1,16 +1,19 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Box } from '@chakra-ui/react';
3
+ import { fn } from '@storybook/test';
3
4
  import { InboxCard } from '../../../components';
4
5
  import { Document } from '../../../assets/images';
5
6
  export default {
6
7
  title: 'Components/Partner/Admin Tiles/Inbox',
7
8
  component: InboxCard,
8
9
  args: {
9
- image: Document,
10
- name: 'Inbox',
11
- newMessages: 10,
12
- showBorder: true,
13
- totalMessages: 32,
10
+ box: {
11
+ image: Document,
12
+ name: 'Inbox',
13
+ newMessages: 10,
14
+ totalMessages: 32,
15
+ },
16
+ onClick: fn(),
14
17
  },
15
18
  };
16
19
  export const InboxCardComponent = (args) => {
@@ -0,0 +1,8 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+ interface NoWidowArgs {
3
+ width: number;
4
+ }
5
+ declare const _default: Meta<NoWidowArgs>;
6
+ export default _default;
7
+ export declare const TitleAndBody: StoryObj<NoWidowArgs>;
8
+ export declare const NeverWrappingTextStaysOnOneLine: StoryObj<NoWidowArgs>;
@@ -0,0 +1,61 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
+ import { Box, Button, Stack, Text } from '@chakra-ui/react';
12
+ import { expect, within } from '@storybook/test';
13
+ export default {
14
+ title: 'Components/Typography/No Widow',
15
+ // A 390px phone screen: without the fix, both the title and the body end on one word.
16
+ args: { width: 390 },
17
+ };
18
+ const TITLE = 'Connect this phone to your Homefile account';
19
+ const BODY = 'We sent a 6-digit code to the email on your account. Enter it here to finish connecting this phone.';
20
+ // How many words sit on the last rendered line of an element's text.
21
+ const wordsOnLastLine = (element) => {
22
+ var _a, _b, _c, _d, _e;
23
+ const rows = new Map();
24
+ const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);
25
+ for (let node = walker.nextNode(); node; node = walker.nextNode()) {
26
+ const text = (_a = node.textContent) !== null && _a !== void 0 ? _a : '';
27
+ for (const match of text.matchAll(/\S+/g)) {
28
+ const range = document.createRange();
29
+ range.setStart(node, (_b = match.index) !== null && _b !== void 0 ? _b : 0);
30
+ range.setEnd(node, ((_c = match.index) !== null && _c !== void 0 ? _c : 0) + match[0].length);
31
+ const top = Math.round(range.getBoundingClientRect().top);
32
+ rows.set(top, ((_d = rows.get(top)) !== null && _d !== void 0 ? _d : 0) + 1);
33
+ }
34
+ }
35
+ return (_e = rows.get(Math.max(...rows.keys()))) !== null && _e !== void 0 ? _e : 0;
36
+ };
37
+ export const TitleAndBody = {
38
+ render: ({ width }) => (_jsx(Box, { w: `${width}px`, p: "base", bg: "neutral.white", children: _jsxs(Stack, { spacing: "base", children: [_jsx(Text, { variant: "title", children: TITLE }), _jsx(Text, { children: BODY })] }) })),
39
+ play: (_a) => __awaiter(void 0, [_a], void 0, function* ({ canvasElement }) {
40
+ const canvas = within(canvasElement);
41
+ const title = canvas.getByText(TITLE);
42
+ const body = canvas.getByText(BODY);
43
+ yield expect(getComputedStyle(title).getPropertyValue('text-wrap-style')).toBe('balance');
44
+ yield expect(getComputedStyle(body).getPropertyValue('text-wrap-style')).toBe('pretty');
45
+ // Neither block ends on a single word.
46
+ yield expect(wordsOnLastLine(title)).toBeGreaterThanOrEqual(2);
47
+ yield expect(wordsOnLastLine(body)).toBeGreaterThanOrEqual(2);
48
+ }),
49
+ };
50
+ const BUTTON_LABEL = 'Set Up Homi on this phone';
51
+ // The no-widow rule only changes HOW text breaks: text that never wraps (inside a
52
+ // button, a badge) must still stay on one line.
53
+ export const NeverWrappingTextStaysOnOneLine = {
54
+ render: () => (_jsx(Box, { w: "120px", p: "base", bg: "neutral.white", children: _jsx(Button, { w: "full", children: _jsx(Text, { children: BUTTON_LABEL }) }) })),
55
+ play: (_a) => __awaiter(void 0, [_a], void 0, function* ({ canvasElement }) {
56
+ const label = within(canvasElement).getByText(BUTTON_LABEL);
57
+ yield expect(getComputedStyle(label).getPropertyValue('text-wrap-mode')).toBe('nowrap');
58
+ const lineHeight = parseFloat(getComputedStyle(label).lineHeight);
59
+ yield expect(label.getBoundingClientRect().height).toBeLessThan(lineHeight * 1.5);
60
+ }),
61
+ };
@@ -2,24 +2,29 @@ export declare const Text: {
2
2
  baseStyle: {
3
3
  fontWeight: string;
4
4
  lineHeight: string;
5
+ textWrapStyle: string;
5
6
  };
6
7
  variants: {
7
8
  heading: {
9
+ textWrapStyle: string;
8
10
  fontSize: string;
9
11
  color: string;
10
12
  lineHeight: string;
11
13
  };
12
14
  heading6: {
15
+ textWrapStyle: string;
13
16
  fontSize: string;
14
17
  color: string;
15
18
  lineHeight: string;
16
19
  };
17
20
  title: {
21
+ textWrapStyle: string;
18
22
  fontSize: string;
19
23
  color: string;
20
24
  lineHeight: string;
21
25
  };
22
26
  titleBolder: {
27
+ textWrapStyle: string;
23
28
  fontSize: string;
24
29
  color: string;
25
30
  fontWeight: string;
@@ -2,24 +2,32 @@ export const Text = {
2
2
  baseStyle: {
3
3
  fontWeight: 'regular',
4
4
  lineHeight: '20px',
5
+ // No widows: running text never ends on a single word. The longhand only
6
+ // changes HOW lines break; the text-wrap shorthand would also undo the
7
+ // nowrap a Text inherits inside a Button or Badge.
8
+ textWrapStyle: 'pretty',
5
9
  },
6
10
  variants: {
7
11
  heading: {
12
+ textWrapStyle: 'balance',
8
13
  fontSize: '3xl',
9
14
  color: 'gray.3',
10
15
  lineHeight: '30px',
11
16
  },
12
17
  heading6: {
18
+ textWrapStyle: 'balance',
13
19
  fontSize: '2xl',
14
20
  color: 'gray.3',
15
21
  lineHeight: '30px',
16
22
  },
17
23
  title: {
24
+ textWrapStyle: 'balance',
18
25
  fontSize: 'lg',
19
26
  color: 'gray.3',
20
27
  lineHeight: '22px',
21
28
  },
22
29
  titleBolder: {
30
+ textWrapStyle: 'balance',
23
31
  fontSize: 'lg',
24
32
  color: 'gray.3',
25
33
  fontWeight: 'semibold',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homefile/components-v2",
3
- "version": "2.62.0",
3
+ "version": "2.63.0",
4
4
  "author": "Homefile",
5
5
  "license": "UNLICENSED",
6
6
  "typings": "dist/index.d.ts",
@@ -71,6 +71,7 @@
71
71
  "@storybook/react": "^8.5.2",
72
72
  "@storybook/react-vite": "^8.5.2",
73
73
  "@storybook/test": "^8.5.2",
74
+ "@storybook/test-runner": "0.21.3",
74
75
  "@types/node": "^22.10.7",
75
76
  "@types/react": "^19.0.7",
76
77
  "@types/react-datepicker": "^7.0.0",
@@ -120,5 +121,9 @@
120
121
  "tsc-alias": "^1.8.16",
121
122
  "valtio": "^2.1.3",
122
123
  "yup": "^1.6.1"
124
+ },
125
+ "resolutions": {
126
+ "playwright": "1.58.2",
127
+ "playwright-core": "1.58.2"
123
128
  }
124
129
  }