From 6618cc92e738f57b396759adcdeab54a096984cf Mon Sep 17 00:00:00 2001 From: Zach Harrison Date: Wed, 17 Jan 2024 14:20:44 -0800 Subject: [PATCH 001/753] start working on tax form views --- .../sqm-tax-form/sqm-tax-form-step-1-view.tsx | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 packages/mint-components/src/components/sqm-tax-form/sqm-tax-form-step-1-view.tsx diff --git a/packages/mint-components/src/components/sqm-tax-form/sqm-tax-form-step-1-view.tsx b/packages/mint-components/src/components/sqm-tax-form/sqm-tax-form-step-1-view.tsx new file mode 100644 index 0000000000..1e9da76ba2 --- /dev/null +++ b/packages/mint-components/src/components/sqm-tax-form/sqm-tax-form-step-1-view.tsx @@ -0,0 +1,88 @@ +import { h } from "@stencil/core"; + +export interface TaxFormStepOneProps { + states: { + loading: boolean; + formState: { + firstName: string; + lastName: string; + email: string; + country: string; + currency: string; + indirectTaxNumber: string; + allowBankingCollection: boolean; + errors: any; + error: string; + }; + }; + callbacks: { + onSubmit: (props: any) => void; + onChange: (e) => void; + }; + text: { + firstName: string; + lastName: string; + email: string; + country: string; + currency: string; + indirectTaxNumber: string; + allowBankingCollection: string; + submitButton: string; + }; +} + +const TaxFormStepOneView = (props: TaxFormStepOneProps) => { + const { + states, + states: { formState }, + callbacks, + text, + } = props; + + return ( +
+ + + + ); +}; + +export default TaxFormStepOneView; From be7653543d9db55b2258eb64756b3f067727eee2 Mon Sep 17 00:00:00 2001 From: Zach Harrison Date: Wed, 17 Jan 2024 15:23:54 -0800 Subject: [PATCH 002/753] Add story for form --- .../sqm-stencilbook/sqm-stencilbook.tsx | 2 + .../sqm-tax-form/TaxForm.stories.tsx | 58 ++++++ .../sqm-tax-form/sqm-tax-form-step-1-view.tsx | 166 ++++++++++++++++-- 3 files changed, 210 insertions(+), 16 deletions(-) create mode 100644 packages/mint-components/src/components/sqm-tax-form/TaxForm.stories.tsx diff --git a/packages/mint-components/src/components/sqm-stencilbook/sqm-stencilbook.tsx b/packages/mint-components/src/components/sqm-stencilbook/sqm-stencilbook.tsx index 56e755fc64..996924a7b7 100644 --- a/packages/mint-components/src/components/sqm-stencilbook/sqm-stencilbook.tsx +++ b/packages/mint-components/src/components/sqm-stencilbook/sqm-stencilbook.tsx @@ -75,6 +75,7 @@ import * as CouponCode from "../sqm-coupon-code/CouponCode.stories"; import * as LogoutCurrentUser from "../sqm-logout-current-user/LogoutCurrentUser.stories"; import * as LinkButton from "../sqm-link-button/LinkButton.stories"; import * as CloseButton from "../sqm-close-button/CloseButton.stories"; +import * as TaxForm from "../sqm-tax-form/TaxForm.stories"; import * as Themes from "./Themes"; import { CucumberAddon } from "./CucumberAddon"; @@ -156,6 +157,7 @@ const stories = [ LinkButton, EmailRegistration, CloseButton, + TaxForm, ]; /** diff --git a/packages/mint-components/src/components/sqm-tax-form/TaxForm.stories.tsx b/packages/mint-components/src/components/sqm-tax-form/TaxForm.stories.tsx new file mode 100644 index 0000000000..d215254f63 --- /dev/null +++ b/packages/mint-components/src/components/sqm-tax-form/TaxForm.stories.tsx @@ -0,0 +1,58 @@ +import { h } from "@stencil/core"; +import { + TaxFormStepOneView, + TaxFormStepOneProps, +} from "./sqm-tax-form-step-1-view"; + +export default { + title: "Components/Tax Form", +}; + +const stepOneProps: TaxFormStepOneProps = { + states: { + loading: false, + submitDisabled: false, + formState: { + firstName: "Bob", + lastName: "Testerson", + email: "bobtesterson@example.com", + country: "US", + currency: "fghdfgsd", + indirectTaxNumber: "sfgdfdgs", + allowBankingCollection: true, + }, + }, + callbacks: { + onSubmit: (props: any) => console.log("Submit"), + onChange: (e) => console.log("Submit"), + }, + text: { + firstName: "First name", + lastName: "Last name", + email: "Email", + country: "Country", + currency: "Currency", + indirectTaxNumber: "Indirect Tax Number", + allowBankingCollection: + "I allow impact.com to collect my tax and banking information", + submitButton: "Continue", + }, +}; + +export const StepOne = () => { + return ( + + ); +}; + +export const StepOneTest = () => { + return ; +}; + +export const Test = () => { + return
Does this work?
; +}; diff --git a/packages/mint-components/src/components/sqm-tax-form/sqm-tax-form-step-1-view.tsx b/packages/mint-components/src/components/sqm-tax-form/sqm-tax-form-step-1-view.tsx index 1e9da76ba2..e0d1596f3c 100644 --- a/packages/mint-components/src/components/sqm-tax-form/sqm-tax-form-step-1-view.tsx +++ b/packages/mint-components/src/components/sqm-tax-form/sqm-tax-form-step-1-view.tsx @@ -3,6 +3,7 @@ import { h } from "@stencil/core"; export interface TaxFormStepOneProps { states: { loading: boolean; + submitDisabled: boolean; formState: { firstName: string; lastName: string; @@ -11,8 +12,8 @@ export interface TaxFormStepOneProps { currency: string; indirectTaxNumber: string; allowBankingCollection: boolean; - errors: any; - error: string; + errors?: any; + error?: string; }; }; callbacks: { @@ -31,7 +32,7 @@ export interface TaxFormStepOneProps { }; } -const TaxFormStepOneView = (props: TaxFormStepOneProps) => { +export const TaxFormStepOneView = (props: TaxFormStepOneProps) => { const { states, states: { formState }, @@ -48,16 +49,16 @@ const TaxFormStepOneView = (props: TaxFormStepOneProps) => { label={text.firstName} disabled={states.loading} // Copied from edit form, may need to keep - // {...(formState.errors.firstName && - // formState.errors.firstName.status !== "valid" - // ? { class: "ErrorStyles", helpText: "Cannot be empty" } + // {...(formState.errors?.firstName && + // formState.errors?.firstName.status !== "valid" + // ? { class: "errors?tyles", helpText: "Cannot be empty" } // : [])} id="firstName" name="firstName" error={ - formState.errors.firstName && - formState.errors.firstName.status !== "valid" - ? formState.errors.firstName.message + formState.errors?.firstName && + formState.errors?.firstName.status !== "valid" + ? formState.errors?.firstName.message : undefined } /> @@ -68,21 +69,154 @@ const TaxFormStepOneView = (props: TaxFormStepOneProps) => { label={text.lastName} disabled={states.loading} // Copied from edit form, may need to keep - // {...(formState.errors.lastName && - // formState.errors.lastName.status !== "valid" - // ? { class: "ErrorStyles", helpText: "Cannot be empty" } + // {...(formState.errors?.lastName && + // formState.errors?.lastName.status !== "valid" + // ? { class: "errors?tyles", helpText: "Cannot be empty" } // : [])} id="lastName" name="lastName" error={ - formState.errors.lastName && - formState.errors.lastName.status !== "valid" - ? formState.errors.lastName.message + formState.errors?.lastName && + formState.errors?.lastName.status !== "valid" + ? formState.errors?.lastName.message : undefined } /> + + + + + + + + { + callbacks.onSubmit(e); + }} + submit + exportparts="base: primarybutton-base" + > + {text.submitButton} + ); }; -export default TaxFormStepOneView; +// export default TaxFormStepOneView; From 1ca6a69acd141c4b92a2593026cf9728d0ff6281 Mon Sep 17 00:00:00 2001 From: Zach Harrison Date: Wed, 17 Jan 2024 16:52:42 -0800 Subject: [PATCH 003/753] add more stories --- .../sqm-tax-form/TaxForm.stories.tsx | 38 +++++++++++++++---- .../sqm-tax-form/sqm-tax-form-step-1-view.tsx | 8 ++-- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/packages/mint-components/src/components/sqm-tax-form/TaxForm.stories.tsx b/packages/mint-components/src/components/sqm-tax-form/TaxForm.stories.tsx index d215254f63..83d45a4084 100644 --- a/packages/mint-components/src/components/sqm-tax-form/TaxForm.stories.tsx +++ b/packages/mint-components/src/components/sqm-tax-form/TaxForm.stories.tsx @@ -40,19 +40,43 @@ const stepOneProps: TaxFormStepOneProps = { }; export const StepOne = () => { + return ; +}; + +export const StepOneLoading = () => { return ( ); }; -export const StepOneTest = () => { - return ; +export const StepOneDisabled = () => { + return ( + + ); }; -export const Test = () => { - return
Does this work?
; +export const StepOneWithError = () => { + return ( + + ); }; diff --git a/packages/mint-components/src/components/sqm-tax-form/sqm-tax-form-step-1-view.tsx b/packages/mint-components/src/components/sqm-tax-form/sqm-tax-form-step-1-view.tsx index e0d1596f3c..9f8f95935b 100644 --- a/packages/mint-components/src/components/sqm-tax-form/sqm-tax-form-step-1-view.tsx +++ b/packages/mint-components/src/components/sqm-tax-form/sqm-tax-form-step-1-view.tsx @@ -182,12 +182,10 @@ export const TaxFormStepOneView = (props: TaxFormStepOneProps) => { : undefined } /> - { ? formState.errors?.allowBankingCollection.message : undefined } - /> + > + {text.allowBankingCollection} + Date: Wed, 17 Jan 2024 17:13:37 -0800 Subject: [PATCH 004/753] a bunch of placeholder components, form routing --- packages/mint-components/src/components.d.ts | 110 +++++++- .../sqm-cash-form/sqm-cash-form.tsx | 49 ++++ .../sqm-context-router/ContextRouter.feature | 61 ++++ .../sqm-context-router/ContextRouter.tsx | 175 ++++++++++++ .../components/sqm-context-router/readme.md | 23 ++ .../sqm-context-router/sqm-context-router.css | 7 + .../sqm-context-router.e2e.ts | 266 ++++++++++++++++++ .../sqm-context-router/sqm-context-router.tsx | 38 +++ .../sqm-context-router/useContextRouter.ts | 97 +++++++ .../sqm-docusign-form/sqm-docusign-form.tsx | 51 ++++ .../sqm-tax-and-cash/TaxAndCash.stories.tsx | 21 ++ .../src/components/sqm-tax-and-cash/readme.md | 32 +++ .../sqm-tax-and-cash-view.tsx | 11 + .../sqm-tax-and-cash/sqm-tax-and-cash.tsx | 58 ++++ .../sqm-tax-and-cash/useTaxAndCash.tsx | 32 +++ .../components/sqm-tax-form/sqm-tax-form.tsx | 58 ++++ .../components/sqm-tax-form/useTaxForm.tsx | 17 ++ packages/mint-components/src/index.html | 16 +- 18 files changed, 1120 insertions(+), 2 deletions(-) create mode 100644 packages/mint-components/src/components/sqm-cash-form/sqm-cash-form.tsx create mode 100644 packages/mint-components/src/components/sqm-context-router/ContextRouter.feature create mode 100644 packages/mint-components/src/components/sqm-context-router/ContextRouter.tsx create mode 100644 packages/mint-components/src/components/sqm-context-router/readme.md create mode 100644 packages/mint-components/src/components/sqm-context-router/sqm-context-router.css create mode 100644 packages/mint-components/src/components/sqm-context-router/sqm-context-router.e2e.ts create mode 100644 packages/mint-components/src/components/sqm-context-router/sqm-context-router.tsx create mode 100644 packages/mint-components/src/components/sqm-context-router/useContextRouter.ts create mode 100644 packages/mint-components/src/components/sqm-docusign-form/sqm-docusign-form.tsx create mode 100644 packages/mint-components/src/components/sqm-tax-and-cash/TaxAndCash.stories.tsx create mode 100644 packages/mint-components/src/components/sqm-tax-and-cash/readme.md create mode 100644 packages/mint-components/src/components/sqm-tax-and-cash/sqm-tax-and-cash-view.tsx create mode 100644 packages/mint-components/src/components/sqm-tax-and-cash/sqm-tax-and-cash.tsx create mode 100644 packages/mint-components/src/components/sqm-tax-and-cash/useTaxAndCash.tsx create mode 100644 packages/mint-components/src/components/sqm-tax-form/sqm-tax-form.tsx create mode 100644 packages/mint-components/src/components/sqm-tax-form/useTaxForm.tsx diff --git a/packages/mint-components/src/components.d.ts b/packages/mint-components/src/components.d.ts index 0e94a4916a..2ccefbed9c 100644 --- a/packages/mint-components/src/components.d.ts +++ b/packages/mint-components/src/components.d.ts @@ -8,6 +8,7 @@ import { HTMLStencilElement, JSXBase } from "@stencil/core/internal"; import { DemoData } from "./global/demo"; import { AssetCardViewProps } from "./components/sqm-asset-card/sqm-asset-card-view"; import { BigStatViewProps } from "./components/sqm-big-stat/sqm-big-stat-view"; +import { UserNameViewProps } from "./components/sqm-tax-and-cash/sqm-tax-and-cash-view"; import { CheckboxFieldViewProps } from "./components/sqm-checkbox-field/sqm-checkbox-field-view"; import { CouponCodeViewProps } from "./components/sqm-coupon-code/sqm-coupon-code-view"; import { DropdownFieldViewProps } from "./components/sqm-dropdown-field/sqm-dropdown-field-view"; @@ -39,7 +40,7 @@ import { ReferralDates } from "./components/sqm-referral-table/useReferralTable" import { RewardExchangeViewProps } from "./components/sqm-reward-exchange-list/sqm-reward-exchange-list-view"; import { ShareButtonViewProps } from "./components/sqm-share-button/sqm-share-button-view"; import { TaskCardViewProps } from "./components/sqm-task-card/sqm-task-card-view"; -import { UserNameViewProps } from "./components/sqm-user-name/sqm-user-name-view"; +import { UserNameViewProps as UserNameViewProps1 } from "./components/sqm-user-name/sqm-user-name-view"; export namespace Components { interface RaisinsPlopTarget { "renderCell": () => Promise; @@ -121,6 +122,13 @@ export namespace Components { */ "width": number; } + interface SqmCashForm { + /** + * @undocumented + * @uiType object + */ + "demoData"?: DemoData; + } interface SqmCheckboxField { /** * @uiName Checkbox label @@ -164,6 +172,9 @@ export namespace Components { */ "color": string; } + interface SqmContextRouter { + "contextName": string; + } interface SqmCouponCode { /** * Set the copy button style and placement. @@ -272,6 +283,13 @@ export namespace Components { */ "dividerStyle": string; } + interface SqmDocusignForm { + /** + * @undocumented + * @uiType object + */ + "demoData"?: DemoData; + } interface SqmDropdownField { /** * @undocumented @@ -2802,6 +2820,20 @@ export namespace Components { */ "steps": boolean; } + interface SqmTaxAndCash { + /** + * @undocumented + * @uiType object + */ + "demoData"?: DemoData; + } + interface SqmTaxForm { + /** + * @undocumented + * @uiType object + */ + "demoData"?: DemoData; + } interface SqmText { } interface SqmTextSpan { @@ -2926,6 +2958,12 @@ declare global { prototype: HTMLSqmCardFeedElement; new (): HTMLSqmCardFeedElement; }; + interface HTMLSqmCashFormElement extends Components.SqmCashForm, HTMLStencilElement { + } + var HTMLSqmCashFormElement: { + prototype: HTMLSqmCashFormElement; + new (): HTMLSqmCashFormElement; + }; interface HTMLSqmCheckboxFieldElement extends Components.SqmCheckboxField, HTMLStencilElement { } var HTMLSqmCheckboxFieldElement: { @@ -2938,6 +2976,12 @@ declare global { prototype: HTMLSqmCloseButtonElement; new (): HTMLSqmCloseButtonElement; }; + interface HTMLSqmContextRouterElement extends Components.SqmContextRouter, HTMLStencilElement { + } + var HTMLSqmContextRouterElement: { + prototype: HTMLSqmContextRouterElement; + new (): HTMLSqmContextRouterElement; + }; interface HTMLSqmCouponCodeElement extends Components.SqmCouponCode, HTMLStencilElement { } var HTMLSqmCouponCodeElement: { @@ -2950,6 +2994,12 @@ declare global { prototype: HTMLSqmDividedLayoutElement; new (): HTMLSqmDividedLayoutElement; }; + interface HTMLSqmDocusignFormElement extends Components.SqmDocusignForm, HTMLStencilElement { + } + var HTMLSqmDocusignFormElement: { + prototype: HTMLSqmDocusignFormElement; + new (): HTMLSqmDocusignFormElement; + }; interface HTMLSqmDropdownFieldElement extends Components.SqmDropdownField, HTMLStencilElement { } var HTMLSqmDropdownFieldElement: { @@ -3418,6 +3468,18 @@ declare global { prototype: HTMLSqmTaskCardElement; new (): HTMLSqmTaskCardElement; }; + interface HTMLSqmTaxAndCashElement extends Components.SqmTaxAndCash, HTMLStencilElement { + } + var HTMLSqmTaxAndCashElement: { + prototype: HTMLSqmTaxAndCashElement; + new (): HTMLSqmTaxAndCashElement; + }; + interface HTMLSqmTaxFormElement extends Components.SqmTaxForm, HTMLStencilElement { + } + var HTMLSqmTaxFormElement: { + prototype: HTMLSqmTaxFormElement; + new (): HTMLSqmTaxFormElement; + }; interface HTMLSqmTextElement extends Components.SqmText, HTMLStencilElement { } var HTMLSqmTextElement: { @@ -3460,10 +3522,13 @@ declare global { "sqm-big-stat": HTMLSqmBigStatElement; "sqm-brand": HTMLSqmBrandElement; "sqm-card-feed": HTMLSqmCardFeedElement; + "sqm-cash-form": HTMLSqmCashFormElement; "sqm-checkbox-field": HTMLSqmCheckboxFieldElement; "sqm-close-button": HTMLSqmCloseButtonElement; + "sqm-context-router": HTMLSqmContextRouterElement; "sqm-coupon-code": HTMLSqmCouponCodeElement; "sqm-divided-layout": HTMLSqmDividedLayoutElement; + "sqm-docusign-form": HTMLSqmDocusignFormElement; "sqm-dropdown-field": HTMLSqmDropdownFieldElement; "sqm-edit-profile": HTMLSqmEditProfileElement; "sqm-empty": HTMLSqmEmptyElement; @@ -3542,6 +3607,8 @@ declare global { "sqm-table-row": HTMLSqmTableRowElement; "sqm-tabs": HTMLSqmTabsElement; "sqm-task-card": HTMLSqmTaskCardElement; + "sqm-tax-and-cash": HTMLSqmTaxAndCashElement; + "sqm-tax-form": HTMLSqmTaxFormElement; "sqm-text": HTMLSqmTextElement; "sqm-text-span": HTMLSqmTextSpanElement; "sqm-timeline": HTMLSqmTimelineElement; @@ -3629,6 +3696,13 @@ declare namespace LocalJSX { */ "width"?: number; } + interface SqmCashForm { + /** + * @undocumented + * @uiType object + */ + "demoData"?: DemoData; + } interface SqmCheckboxField { /** * @uiName Checkbox label @@ -3672,6 +3746,9 @@ declare namespace LocalJSX { */ "color"?: string; } + interface SqmContextRouter { + "contextName"?: string; + } interface SqmCouponCode { /** * Set the copy button style and placement. @@ -3780,6 +3857,13 @@ declare namespace LocalJSX { */ "dividerStyle"?: string; } + interface SqmDocusignForm { + /** + * @undocumented + * @uiType object + */ + "demoData"?: DemoData; + } interface SqmDropdownField { /** * @undocumented @@ -6286,6 +6370,20 @@ declare namespace LocalJSX { */ "steps"?: boolean; } + interface SqmTaxAndCash { + /** + * @undocumented + * @uiType object + */ + "demoData"?: DemoData; + } + interface SqmTaxForm { + /** + * @undocumented + * @uiType object + */ + "demoData"?: DemoData; + } interface SqmText { } interface SqmTextSpan { @@ -6383,10 +6481,13 @@ declare namespace LocalJSX { "sqm-big-stat": SqmBigStat; "sqm-brand": SqmBrand; "sqm-card-feed": SqmCardFeed; + "sqm-cash-form": SqmCashForm; "sqm-checkbox-field": SqmCheckboxField; "sqm-close-button": SqmCloseButton; + "sqm-context-router": SqmContextRouter; "sqm-coupon-code": SqmCouponCode; "sqm-divided-layout": SqmDividedLayout; + "sqm-docusign-form": SqmDocusignForm; "sqm-dropdown-field": SqmDropdownField; "sqm-edit-profile": SqmEditProfile; "sqm-empty": SqmEmpty; @@ -6465,6 +6566,8 @@ declare namespace LocalJSX { "sqm-table-row": SqmTableRow; "sqm-tabs": SqmTabs; "sqm-task-card": SqmTaskCard; + "sqm-tax-and-cash": SqmTaxAndCash; + "sqm-tax-form": SqmTaxForm; "sqm-text": SqmText; "sqm-text-span": SqmTextSpan; "sqm-timeline": SqmTimeline; @@ -6482,10 +6585,13 @@ declare module "@stencil/core" { "sqm-big-stat": LocalJSX.SqmBigStat & JSXBase.HTMLAttributes; "sqm-brand": LocalJSX.SqmBrand & JSXBase.HTMLAttributes; "sqm-card-feed": LocalJSX.SqmCardFeed & JSXBase.HTMLAttributes; + "sqm-cash-form": LocalJSX.SqmCashForm & JSXBase.HTMLAttributes; "sqm-checkbox-field": LocalJSX.SqmCheckboxField & JSXBase.HTMLAttributes; "sqm-close-button": LocalJSX.SqmCloseButton & JSXBase.HTMLAttributes; + "sqm-context-router": LocalJSX.SqmContextRouter & JSXBase.HTMLAttributes; "sqm-coupon-code": LocalJSX.SqmCouponCode & JSXBase.HTMLAttributes; "sqm-divided-layout": LocalJSX.SqmDividedLayout & JSXBase.HTMLAttributes; + "sqm-docusign-form": LocalJSX.SqmDocusignForm & JSXBase.HTMLAttributes; "sqm-dropdown-field": LocalJSX.SqmDropdownField & JSXBase.HTMLAttributes; "sqm-edit-profile": LocalJSX.SqmEditProfile & JSXBase.HTMLAttributes; "sqm-empty": LocalJSX.SqmEmpty & JSXBase.HTMLAttributes; @@ -6564,6 +6670,8 @@ declare module "@stencil/core" { "sqm-table-row": LocalJSX.SqmTableRow & JSXBase.HTMLAttributes; "sqm-tabs": LocalJSX.SqmTabs & JSXBase.HTMLAttributes; "sqm-task-card": LocalJSX.SqmTaskCard & JSXBase.HTMLAttributes; + "sqm-tax-and-cash": LocalJSX.SqmTaxAndCash & JSXBase.HTMLAttributes; + "sqm-tax-form": LocalJSX.SqmTaxForm & JSXBase.HTMLAttributes; "sqm-text": LocalJSX.SqmText & JSXBase.HTMLAttributes; "sqm-text-span": LocalJSX.SqmTextSpan & JSXBase.HTMLAttributes; "sqm-timeline": LocalJSX.SqmTimeline & JSXBase.HTMLAttributes; diff --git a/packages/mint-components/src/components/sqm-cash-form/sqm-cash-form.tsx b/packages/mint-components/src/components/sqm-cash-form/sqm-cash-form.tsx new file mode 100644 index 0000000000..fda98f7dbd --- /dev/null +++ b/packages/mint-components/src/components/sqm-cash-form/sqm-cash-form.tsx @@ -0,0 +1,49 @@ +import { withHooks } from "@saasquatch/stencil-hooks"; +import { Component, h, Host, Prop, State } from "@stencil/core"; +import deepmerge from "deepmerge"; +import { DemoData } from "../../global/demo"; +import { UserNameViewProps } from "../sqm-tax-and-cash/sqm-tax-and-cash-view"; +import { LoadingSkeleton } from "../../tables/TableSlots"; +import { useTaxForm } from "../sqm-tax-form/useTaxForm"; + +/** + * @uiName Tax And Cash + * @exampleGroup Common Components + * @example User Name Display - + */ +@Component({ + tag: "sqm-cash-form", + shadow: false, +}) +export class CashForm { + @State() ignored = true; + + /** + * @undocumented + * @uiType object + */ + @Prop() demoData?: DemoData; + + constructor() { + withHooks(this); + } + + disconnectedCallback() {} + + render() { + const props = useTaxForm(); + + console.log({ props }); + if (props.loading) return ; + return ( + + Step 3 props.setStep("/2")}>back + props.setStep("/4")}>continue + + ); + } +} + +function useTaxAndCashDemo(props: CashForm) { + return deepmerge({}, props.demoData || {}, { arrayMerge: (_, a) => a }); +} diff --git a/packages/mint-components/src/components/sqm-context-router/ContextRouter.feature b/packages/mint-components/src/components/sqm-context-router/ContextRouter.feature new file mode 100644 index 0000000000..bdad095cb3 --- /dev/null +++ b/packages/mint-components/src/components/sqm-context-router/ContextRouter.feature @@ -0,0 +1,61 @@ +Feature: Router + + The router allows routing between different components through a virtual history + + Scenario: Default route + Given there is a route for "/" + And there is a route for "/stuff" + When the router renders + Then the route for "/" will be displayed + + Scenario: Changing pages + Given there is a route for "/" + And there is a route for "/stuff" + When the router renders + Then the route for "/" will be displayed + When "/stuff" is pushed to squatchHistory + Then the route for "/stuff" will be displayed + When "/" is pushed to squatchHistory + Then the route for "/" will be displayed + + Scenario: Going back + Given there is a route for "/" + And there is a route for "/stuff" + And there is a route for "/things" + When the router renders + Then the route for "/" will be displayed + When "/stuff" is pushed to squatchHistory + Then the route for "/stuff" will be displayed + When "/things" is pushed to squatchHistory + Then the route for "/things" will be displayed + When squatchHistory pops an element + Then the route for "/stuff" will be displayed + When squatchHistory pops an element + Then the route for "/" will be displayed + + Scenario: Template has precedence over route + Given there is a route for "/" + And there is a template for "/" + And there is a route for "/stuff" + When the router renders + Then the template for "/" will be rendered + When "/stuff" is pushed to squatchHistory + Then the route for "/stuff" will be displayed + When "/" is pushed to squatchHistory + Then the template for "/" will be displayed + + Scenario: First matching element is chosen, with precedence + Given the following elements are declared in order on the DOM: + | type | path | id | + | route | / | RootA | + | route | / | RootB | + | template | / | RootC | + | template | / | RootD | + | route | /stuff | StuffA | + | route | /stuff | StuffB | + When the router renders + Then RootC will be rendered + When "/stuff" is pushed to squatchHistory + Then StuffA will be rendered + When "/" is pushed to squatchHistory + Then RootC will be rendered \ No newline at end of file diff --git a/packages/mint-components/src/components/sqm-context-router/ContextRouter.tsx b/packages/mint-components/src/components/sqm-context-router/ContextRouter.tsx new file mode 100644 index 0000000000..ff1571a30d --- /dev/null +++ b/packages/mint-components/src/components/sqm-context-router/ContextRouter.tsx @@ -0,0 +1,175 @@ +import { navigation } from "@saasquatch/component-boilerplate"; +import { useState } from "@saasquatch/stencil-hooks"; +import { h } from "@stencil/core"; +import { createHookStory } from "../sqm-stencilbook/HookStoryAddon"; + +export default { + title: "Tests/Router", +}; + +const templates = ` + + + + +`; + +const routes = ` +

foo

+ +

These elements don't produce a specific box by themselves. They are replaced by their pseudo-box and their child boxes. Please note that the CSS Display Level 3 spec defines how the contents value should affect "unusual elements" — elements that aren’t rendered purely by CSS box concepts such as replaced elements. See Appendix B: Effects of display: contents on Unusual Elements for more details. + + Due to a bug in browsers this will currently remove the element from the accessibility tree — screen readers will not look at what's inside. See the Accessibility concerns section below for more details.

+
+

bar

+

Turns off the display of an element so that it has no effect on layout (the document is rendered as though the element did not exist). All descendant elements also have their display turned off. + To have an element take up the space that it would normally take, but without actually rendering anything, use the visibility property instead.

+
+

baz/bang

+
+    ▄▄▄▄▄▄▄░▄▄▄▄▄▄▄░▄▄▄▄▄▄░▄▄▄▄▄
+    ░░▀███░░░░▀██░░░░██▀░░░░██░░
+    ░░░▀██░░░░░▀██░░▄█░░░░░▄█░░░
+    ░░░░███░░░░░▀██▄█░░░░░░█░░░░
+    ░░░░░███░░░░░▀██░░░░░░█▀░░░░
+    ░░░░░░███░░░░▄███░░░░█▀░░░░░
+    ░░░░░░░██▄░░▄▀░███░░█▀░░░░░░
+    ░░░░░░░▀██▄█▀░░░███▄▀░░░░░░░
+    ░░░░░░░░▀██▀░░░░░███░░░░░░░░
+    ░░░░░░░░░▀▀░░░░░░░▀░░░░░░░░░
+    
+
+

/refer/:page

+
+      refer/:page
+    
+
+`; + +export const TemplateNavigation = createHookStory(() => { + return ( +
+ + + + + + + + + +
+ +
+ ); +}); + +export const RouteNavigation = createHookStory(() => { + return ( +
+ + + + + + + + + +
+ +
+ ); +}); + +export const Styling = createHookStory(() => { + return ( +
+ + +
+
+ + +
Column 1
+
Column 2
+ `} + >
+
+
+ ); +}); + +function useTemplate(templateString: string) { + const [editedTemplate, setEditedTemplate] = useState(templateString); + const [previewTemplate, setPreviewTemplate] = useState(templateString); + function setPath(e: Event) { + //@ts-ignore + navigation.push(e.target.value); + } + return { + states: { previewTemplate, editedTemplate }, + callbacks: { setEditedTemplate, setPreviewTemplate, setPath }, + }; +} + +const defaultRouter = ` + + + +`; + +function TemplateView(props) { + const { states, callbacks } = props; + return [ + , + , + , +
, + ]; +} + +export const RouterPlayground = createHookStory(() => { + const { states, callbacks } = useTemplate(defaultRouter); + return ; +}); diff --git a/packages/mint-components/src/components/sqm-context-router/readme.md b/packages/mint-components/src/components/sqm-context-router/readme.md new file mode 100644 index 0000000000..366f4e036e --- /dev/null +++ b/packages/mint-components/src/components/sqm-context-router/readme.md @@ -0,0 +1,23 @@ +# sqm-router + + + + + + +## Dependencies + +### Used by + + - [sqm-stencilbook](../sqm-stencilbook) + +### Graph +```mermaid +graph TD; + sqm-stencilbook --> sqm-router + style sqm-router fill:#f9f,stroke:#333,stroke-width:4px +``` + +---------------------------------------------- + +*Built with [StencilJS](https://stenciljs.com/)* diff --git a/packages/mint-components/src/components/sqm-context-router/sqm-context-router.css b/packages/mint-components/src/components/sqm-context-router/sqm-context-router.css new file mode 100644 index 0000000000..fdb1c96c3f --- /dev/null +++ b/packages/mint-components/src/components/sqm-context-router/sqm-context-router.css @@ -0,0 +1,7 @@ +sqm-router { + /** + Acts like a slot, doesn't provide it's own box + see: https://developer.mozilla.org/en-US/docs/Web/CSS/display#box + **/ + display: contents; +} diff --git a/packages/mint-components/src/components/sqm-context-router/sqm-context-router.e2e.ts b/packages/mint-components/src/components/sqm-context-router/sqm-context-router.e2e.ts new file mode 100644 index 0000000000..3a6e29842d --- /dev/null +++ b/packages/mint-components/src/components/sqm-context-router/sqm-context-router.e2e.ts @@ -0,0 +1,266 @@ +import { E2EPage, newE2EPage } from "@stencil/core/testing"; + +function newRoute(id: string, path: string) { + return /*html*/ ` + +
+
`; +} + +function newTemplate(id: string, path: string) { + return /*html*/ ` + `; +} + +function newPageFunctions(page: Readonly) { + return { + expectElement: async (selector: string) => + // convert to string because jest pretty printing the object causes errors + expect((await page.find(selector))?.id).not.toBeUndefined(), + dontExpectElement: async (selector: string) => + expect((await page.find(selector))?.id).toBeUndefined(), + expectTemplate: async (selector: string) => + expect((await page.find("#" + selector))?.id).not.toBeUndefined(), + dontExpectTemplate: async (selector: string) => + expect((await page.find("#" + selector))?.id).toBeUndefined(), + expectRoute: async (selector: string) => + expect( + (await page.find('div[style="display: contents;"] > div#' + selector)) + ?.id + ).not.toBeUndefined(), + dontExpectRoute: async (selector: string) => + expect( + (await page.find('div[style="display: contents;"] > div#' + selector)) + ?.id + ).toBeUndefined(), + history: { + push: (path: string) => + page.evaluate((x) => window.squatchHistory.push(x), path), + back: () => page.evaluate(() => window.squatchHistory.back()), + }, + }; +} + +describe("sqm-router", () => { + test("Default route", async () => { + const html = /*html*/ ` + + ${newRoute("RouteA", "/")} + ${newRoute("RouteB", "/B")} + + `; + + const page = await newE2EPage(); + await page.setContent(html); + + const { expectElement, expectRoute, dontExpectRoute } = newPageFunctions( + page + ); + + await expectElement("sqm-router"); + + await expectRoute("RouteA"); + await dontExpectRoute("RouteB"); + + page.close(); + }); + + test("Changing pages", async () => { + const html = /*html*/ ` + + ${newRoute("RouteA", "/")} + ${newRoute("RouteB", "/B")} + + `; + + const page = await newE2EPage(); + await page.setContent(html); + + const { + expectElement, + expectRoute, + dontExpectRoute, + history, + } = newPageFunctions(page); + + await expectElement("sqm-router"); + + await expectRoute("RouteA"); + await dontExpectRoute("RouteB"); + + await history.push("/B"); + await page.waitForChanges(); + + await dontExpectRoute("RouteA"); + await expectRoute("RouteB"); + + await history.push("/"); + await page.waitForChanges(); + + await expectRoute("RouteA"); + await dontExpectRoute("RouteB"); + + page.close(); + }); + + test("Going back", async () => { + const html = /*html*/ ` + + ${newRoute("RouteA", "/")} + ${newRoute("RouteB", "/B")} + ${newRoute("RouteC", "/C")} + + `; + + const page = await newE2EPage(); + await page.setContent(html); + + const { + expectElement, + expectRoute, + dontExpectRoute, + history, + } = newPageFunctions(page); + + await expectElement("sqm-router"); + + await expectRoute("RouteA"); + await dontExpectRoute("RouteB"); + await dontExpectRoute("RouteC"); + + await history.push("/B"); + await page.waitForChanges(); + + await dontExpectRoute("RouteA"); + await expectRoute("RouteB"); + await dontExpectRoute("RouteC"); + + await history.push("/C"); + await page.waitForChanges(); + + await dontExpectRoute("RouteA"); + await dontExpectRoute("RouteB"); + await expectRoute("RouteC"); + + await history.back(); + await page.waitForChanges(); + + await dontExpectRoute("RouteA"); + await expectRoute("RouteB"); + await dontExpectRoute("RouteC"); + + await history.back(); + await page.waitForChanges(); + + await expectRoute("RouteA"); + await dontExpectRoute("RouteB"); + await dontExpectRoute("RouteC"); + + page.close(); + }); + + test("Template has precedence over route", async () => { + const html = /*html*/ ` + + ${newRoute("RouteA", "/")} + ${newTemplate("RouteB", "/")} + ${newRoute("RouteC", "/B")} + + `; + + const page = await newE2EPage(); + await page.setContent(html); + + const { + expectElement, + expectTemplate, + dontExpectTemplate, + expectRoute, + dontExpectRoute, + history, + } = newPageFunctions(page); + + await expectElement("sqm-router"); + + await dontExpectRoute("RouteA"); + await expectTemplate("RouteB"); + await dontExpectRoute("RouteC"); + + await history.push("/B"); + await page.waitForChanges(); + + await dontExpectRoute("RouteA"); + await dontExpectTemplate("RouteB"); + await expectRoute("RouteC"); + + await history.push("/"); + await page.waitForChanges(); + + await dontExpectRoute("RouteA"); + await expectTemplate("RouteB"); + await dontExpectRoute("RouteC"); + + page.close(); + }); + + test("First matching element is chosen, with precedence", async () => { + const html = /*html*/ ` + + ${newRoute("RootA", "/")} + ${newRoute("RootB", "/")} + ${newTemplate("RootC", "/")} + ${newTemplate("RootD", "/")} + ${newRoute("StuffA", "/stuff")} + ${newRoute("StuffB", "/stuff")} + + `; + + const page = await newE2EPage(); + await page.setContent(html); + + const { + expectElement, + expectTemplate, + dontExpectTemplate, + expectRoute, + dontExpectRoute, + history, + } = newPageFunctions(page); + + await expectElement("sqm-router"); + + await dontExpectRoute("RootA"); + await dontExpectRoute("RootB"); + await expectTemplate("RootC"); + await dontExpectTemplate("RootD"); + await dontExpectRoute("StuffA"); + await dontExpectRoute("StuffB"); + + await history.push("/stuff"); + await page.waitForChanges(); + + await dontExpectRoute("RootA"); + await dontExpectRoute("RootB"); + await dontExpectTemplate("RootC"); + await dontExpectTemplate("RootD"); + await expectRoute("StuffA"); + await dontExpectRoute("StuffB"); + + await history.push("/"); + await page.waitForChanges(); + + await dontExpectRoute("RootA"); + await dontExpectRoute("RootB"); + await expectTemplate("RootC"); + await dontExpectTemplate("RootD"); + await dontExpectRoute("StuffA"); + await dontExpectRoute("StuffB"); + + page.close(); + }); +}); + +// nice debugging tool +// console.log(await page.evaluate(()=>document.body.innerHTML)) diff --git a/packages/mint-components/src/components/sqm-context-router/sqm-context-router.tsx b/packages/mint-components/src/components/sqm-context-router/sqm-context-router.tsx new file mode 100644 index 0000000000..d8bc9b0db9 --- /dev/null +++ b/packages/mint-components/src/components/sqm-context-router/sqm-context-router.tsx @@ -0,0 +1,38 @@ +import { withHooks } from "@saasquatch/stencil-hooks"; +import { Component, Host, Prop, State, h } from "@stencil/core"; +import { getProps } from "../../utils/utils"; +import { useContextRouter } from "./useContextRouter"; + +/** + * @uiName Context Router + * @slots [{"name":"","title":"Routes"}] + */ +@Component({ + tag: "sqm-context-router", + styleUrl: "sqm-context-router.css", +}) +export class SqmContextRouter { + @State() + ignored = true; + + @Prop() + contextName: string; + + constructor() { + withHooks(this); + } + + disconnectedCallback() {} + + render() { + const { callbacks } = useContextRouter(getProps(this)); + return ( + +
+ +
+
+
+ ); + } +} diff --git a/packages/mint-components/src/components/sqm-context-router/useContextRouter.ts b/packages/mint-components/src/components/sqm-context-router/useContextRouter.ts new file mode 100644 index 0000000000..9277bbfb27 --- /dev/null +++ b/packages/mint-components/src/components/sqm-context-router/useContextRouter.ts @@ -0,0 +1,97 @@ +import { useCurrentPage, useHost } from "@saasquatch/component-boilerplate"; +import { useEffect, useState } from "@saasquatch/universal-hooks"; +import debugFn from "debug"; +import { pathToRegexp } from "path-to-regexp"; +import { SqmContextRouter } from "./sqm-context-router"; +import { useDomContext } from "@saasquatch/stencil-hooks"; +const debug = debugFn("sq:useRouter"); + +export type Route = { + path: string; +}; + +function matchPath(pattern: string, page: string) { + if (!pattern) return; + const regexp = pathToRegexp(pattern); + return regexp.exec(page); +} + +export function useContextRouter(props: SqmContextRouter) { + const host = useHost(); + const [slot, setSlot] = useState(undefined); + const [container, setContainer] = useState(undefined); + + const context = props.contextName; + + const page = useDomContext(context); + + console.log({ page }); + + // convert sqm-routes into templates + useEffect(() => { + const routes = host.querySelectorAll(`sqm-route`); + const routesArray = Array.from(routes); + routesArray.forEach((route) => { + const newTemplate = document.createElement("template"); + newTemplate.setAttribute("path", route.path); + newTemplate.innerHTML = route.innerHTML; + route.parentNode.appendChild(newTemplate); + route.parentNode.removeChild(route); + }); + }, []); + + useEffect(() => { + if (!container || !slot) { + debug("DOM not ready for navigation rendering on:", page); + return; + } + + //