Skip to content

Commit

Permalink
Merge pull request #195 from Foxy/beta
Browse files Browse the repository at this point in the history
chore: release v1.36.0
  • Loading branch information
brettflorio authored Jan 16, 2025
2 parents 3b5ec71 + d86716a commit cc06da6
Show file tree
Hide file tree
Showing 57 changed files with 3,502 additions and 4,266 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 18
node-version: 20
- name: Install dependencies
run: npm ci
- name: Build
Expand Down
440 changes: 406 additions & 34 deletions custom-elements.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"prepack": "npm run lint && rimraf dist && node ./.build/compile-for-npm.js && rollup -c"
},
"dependencies": {
"@foxy.io/sdk": "^1.12.0",
"@foxy.io/sdk": "^1.13.0",
"@open-wc/lit-helpers": "^0.3.12",
"@open-wc/scoped-elements": "^1.2.1",
"@polymer/iron-icons": "^3.0.1",
Expand All @@ -38,6 +38,7 @@
"cookie-storage": "^6.1.0",
"dedent": "^1.5.3",
"email-validator": "^2.0.4",
"highlight.js": "^10.7.3",
"html-entities": "^2.4.0",
"i18next": "^19.7.0",
"i18next-http-backend": "^1.0.18",
Expand All @@ -49,7 +50,6 @@
"uainfer": "^0.5.0",
"vanilla-hcaptcha": "^1.0.2",
"webcomponent-qr-code": "^1.0.5",
"highlight.js": "^10.7.3",
"xstate": "^4.16.0"
},
"devDependencies": {
Expand Down Expand Up @@ -160,4 +160,4 @@
"publishConfig": {
"access": "public"
}
}
}
4 changes: 2 additions & 2 deletions src/elements/internal/InternalForm/InternalForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,8 @@ export class InternalForm<TData extends HALJSONResource> extends Base<TData> {
<div
class=${classMap({
'space-y-m': true,
'transition-opacity': true,
'opacity-0 pointer-events-none': isSpinnerVisible,
'transition-all filter': true,
'opacity-30 blur-sm pointer-events-none': isSpinnerVisible,
})}
>
${this.__generalErrors.map(err => this.__renderGeneralError(err))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,6 @@ describe('AdminSubscriptionForm', () => {

expect(control?.localName).to.equal('foxy-internal-date-control');
expect(control).to.have.attribute('layout', 'summary-item');
expect(control).to.have.attribute('format', 'iso-long');
});

it('renders frequency control inside of the general summary control', async () => {
Expand Down Expand Up @@ -233,7 +232,6 @@ describe('AdminSubscriptionForm', () => {

expect(control?.localName).to.equal('foxy-internal-date-control');
expect(control).to.have.attribute('layout', 'summary-item');
expect(control).to.have.attribute('format', 'iso-long');
});

it('renders date control for end date inside of the general summary control', async () => {
Expand All @@ -253,7 +251,6 @@ describe('AdminSubscriptionForm', () => {

expect(control?.localName).to.equal('foxy-internal-date-control');
expect(control).to.have.attribute('layout', 'summary-item');
expect(control).to.have.attribute('format', 'iso-long');
});

it('renders summary control with overdue information', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,21 +53,17 @@ export class AdminSubscriptionForm extends Base<Data> {
</foxy-internal-admin-subscription-form-error>
<foxy-internal-summary-control infer="general">
<foxy-internal-date-control layout="summary-item" format="iso-long" infer="start-date">
<foxy-internal-date-control layout="summary-item" infer="start-date">
</foxy-internal-date-control>
<foxy-internal-frequency-control
layout="summary-item"
infer="frequency"
allow-twice-a-month
>
</foxy-internal-frequency-control>
<foxy-internal-date-control
layout="summary-item"
format="iso-long"
infer="next-transaction-date"
>
<foxy-internal-date-control layout="summary-item" infer="next-transaction-date">
</foxy-internal-date-control>
<foxy-internal-date-control layout="summary-item" format="iso-long" infer="end-date">
<foxy-internal-date-control layout="summary-item" infer="end-date">
</foxy-internal-date-control>
</foxy-internal-summary-control>
Expand Down
11 changes: 1 addition & 10 deletions src/elements/public/CartForm/types.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,4 @@
import type { Resource } from '@foxy.io/sdk/core';
import type { Rels } from '@foxy.io/sdk/backend';

// TODO remove this once SDK is fixed
type OriginalData = Resource<Rels.Cart, { zoom: ['discounts'] }>;
type FixedData = Omit<OriginalData, 'billing_region' | 'shipping_region'> & {
/** Corresponds to the `region` field in `fx:customer_address`. API quirk. */
billing_state: string;
/** Corresponds to the `region` field in `fx:customer_address`. API quirk. */
shipping_state: string;
};

export type Data = FixedData;
export type Data = Resource<Rels.Cart, { zoom: ['discounts'] }>;
2 changes: 0 additions & 2 deletions src/elements/public/CustomerForm/CustomerForm.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,6 @@ describe('CustomerForm', () => {
form.data = { ...data, first_name: '', last_name: '' };
expect(form.headerTitleOptions).to.have.property('context', 'no_name');

// TODO: remove this when SDK types are fixed
// @ts-expect-error SDK types are incomplete
form.data = { ...data, first_name: null, last_name: null };
expect(form.headerTitleOptions).to.have.property('context', 'no_name');
});
Expand Down
66 changes: 65 additions & 1 deletion src/elements/public/CustomerPortal/CustomerPortal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,42 @@ import { CustomerPortal } from './CustomerPortal';
import { TransactionsTable } from '../TransactionsTable/TransactionsTable';
import { InternalCustomerPortalLoggedInView } from './InternalCustomerPortalLoggedInView';
import { InternalCustomerPortalLoggedOutView } from './InternalCustomerPortalLoggedOutView';
import { InternalCustomerPortalPasswordResetView } from './InternalCustomerPortalPasswordResetView';

describe('CustomerPortal', () => {
before(() => localStorage.clear());

it('extends CustomerApi', () => {
expect(new CustomerPortal()).to.be.instanceOf(CustomerApi);
});

it('imports and defines dependencies', () => {
expect(customElements.get('iron-icon')).to.exist;
expect(customElements.get('vaadin-button')).to.exist;
expect(customElements.get('foxy-internal-password-control')).to.exist;
expect(customElements.get('foxy-internal-sandbox')).to.exist;
expect(customElements.get('foxy-internal-form')).to.exist;
expect(customElements.get('foxy-access-recovery-form')).to.exist;
expect(customElements.get('foxy-payment-method-card')).to.exist;
expect(customElements.get('foxy-transactions-table')).to.exist;
expect(customElements.get('foxy-subscription-card')).to.exist;
expect(customElements.get('foxy-subscription-form')).to.exist;
expect(customElements.get('foxy-collection-pages')).to.exist;
expect(customElements.get('foxy-collection-page')).to.exist;
expect(customElements.get('foxy-customer-form')).to.exist;
expect(customElements.get('foxy-sign-in-form')).to.exist;
expect(customElements.get('foxy-form-dialog')).to.exist;
expect(customElements.get('foxy-spinner')).to.exist;
expect(customElements.get('foxy-i18n')).to.exist;
expect(customElements.get('foxy-customer')).to.exist;
expect(customElements.get('foxy-internal-customer-portal-logged-in-view')).to.exist;
expect(customElements.get('foxy-internal-customer-portal-logged-out-view')).to.exist;
expect(customElements.get('foxy-internal-customer-portal-password-reset-view')).to.exist;
expect(customElements.get('foxy-internal-customer-portal-subscriptions')).to.exist;
expect(customElements.get('foxy-internal-customer-portal-transactions')).to.exist;
expect(customElements.get('foxy-internal-customer-portal-link')).to.exist;
});

it('registers as foxy-customer-portal', () => {
expect(customElements.get('foxy-customer-portal')).to.equal(CustomerPortal);
});
Expand Down Expand Up @@ -84,7 +114,15 @@ describe('CustomerPortal', () => {
});

it('renders foxy-internal-customer-portal-logged-in-view when logged in', async () => {
localStorage.setItem(API.SESSION, 'session-stub');
localStorage.setItem(
API.SESSION,
JSON.stringify({
force_password_reset: false,
session_token: `dasjhf348tuhrgskjfhw48ourowi4rshajdhf`,
expires_in: 2419200,
jwt: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.Et9HFtf9R3GEMA0IICOfFMVXY7kkTX1wr4qCyhIf58U',
})
);

const transactionsTableColumns = [TransactionsTable.idColumn];

Expand All @@ -110,4 +148,30 @@ describe('CustomerPortal', () => {

localStorage.clear();
});

it('renders foxy-internal-customer-portal-password-reset-view when logged in with temporary password', async () => {
localStorage.setItem(
API.SESSION,
JSON.stringify({
force_password_reset: true,
session_token: `dasjhf348tuhrgskjfhw48ourowi4rshajdhf`,
expires_in: 2419200,
jwt: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.Et9HFtf9R3GEMA0IICOfFMVXY7kkTX1wr4qCyhIf58U',
})
);

const layout = html`
<foxy-customer-portal base="https://demo.api/portal/"></foxy-customer-portal>
`;

const element = await fixture<CustomerPortal>(layout);
const view = element.renderRoot.firstElementChild as InternalCustomerPortalPasswordResetView;

expect(view).to.be.instanceOf(InternalCustomerPortalPasswordResetView);
expect(view).to.have.property('localName', 'foxy-internal-customer-portal-password-reset-view');
expect(view).to.have.attribute('href', 'https://demo.api/portal/');
expect(view).to.have.attribute('infer', 'password-reset');

localStorage.clear();
});
});
56 changes: 45 additions & 11 deletions src/elements/public/CustomerPortal/CustomerPortal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { CustomerApi } from '../CustomerApi/CustomerApi';
import { ThemeableMixin } from '../../../mixins/themeable';
import { TranslatableMixin } from '../../../mixins/translatable';
import { TransactionsTable } from '../TransactionsTable/TransactionsTable';
import { UpdateEvent } from '../NucleonElement/UpdateEvent';
import { ifDefined } from 'lit-html/directives/if-defined';

export class CustomerPortal extends TranslatableMixin(
Expand All @@ -15,6 +16,7 @@ export class CustomerPortal extends TranslatableMixin(
return {
...super.properties,
transactionsTableColumns: { attribute: false },
skipPasswordReset: { type: Boolean, attribute: 'skip-password-reset' },
embedUrl: { attribute: 'embed-url' },
group: { type: String },
};
Expand All @@ -30,6 +32,11 @@ export class CustomerPortal extends TranslatableMixin(
TransactionsTable.receiptColumn,
];

#temporaryPassword: string | null = null;

/** When set to true, portal won't display Password Reset screen if customer logs in with a temporary password. */
skipPasswordReset = false;

/**
* URL of the Payment Card Embed for updating payment method.
* When set, the payment method will be editable. Otherwise, the customers
Expand Down Expand Up @@ -58,24 +65,51 @@ export class CustomerPortal extends TranslatableMixin(
}

return this.api.storage.getItem(API.SESSION)
? html`
<foxy-internal-customer-portal-logged-in-view
embed-url=${ifDefined(this.embedUrl ?? void 0)}
customer=${this.base}
class="h-full"
infer=""
href=${ifDefined(settingsHref?.toString())}
.transactionsTableColumns=${this.transactionsTableColumns}
>
</foxy-internal-customer-portal-logged-in-view>
`
? !this.skipPasswordReset && this.api.usesTemporaryPassword
? html`
<foxy-internal-customer-portal-password-reset-view
password-old=${ifDefined(this.#temporaryPassword ?? void 0)}
infer="password-reset"
href=${this.base}
@update=${(evt: UpdateEvent) => {
if (evt.detail?.result === UpdateEvent.UpdateResult.ResourceUpdated) {
this.api.usesTemporaryPassword = false;
this.#temporaryPassword = null;
this.requestUpdate();
}
}}
>
</foxy-internal-customer-portal-password-reset-view>
`
: html`
<foxy-internal-customer-portal-logged-in-view
embed-url=${ifDefined(this.embedUrl ?? void 0)}
customer=${this.base}
class="h-full"
infer=""
href=${ifDefined(settingsHref?.toString())}
.transactionsTableColumns=${this.transactionsTableColumns}
>
</foxy-internal-customer-portal-logged-in-view>
`
: html`
<foxy-internal-customer-portal-logged-out-view
class="h-full"
infer=""
href=${ifDefined(settingsHref?.toString())}
@password=${(evt: CustomEvent<string | null>) => {
this.#temporaryPassword = evt.detail;
}}
>
</foxy-internal-customer-portal-logged-out-view>
`;
}

updated(changedProperties: Map<keyof this, unknown>): void {
super.updated(changedProperties);
const isLoggedIn = this.api.storage.getItem(API.SESSION) !== null;
if (isLoggedIn && (this.skipPasswordReset || !this.api.usesTemporaryPassword)) {
this.#temporaryPassword = null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,12 @@ export class InternalCustomerPortalLoggedOutView extends Base<Data> {
id="sign-in-form"
ns="${this.ns} ${customElements.get('foxy-sign-in-form')?.defaultNS ?? ''}"
.templates=${this.getNestedTemplates('sign-in:form')}
@update=${() => this.requestUpdate()}
@update=${(evt: CustomEvent) => {
const target = evt.currentTarget as SignInForm;
const password = target.form.credential?.password ?? null;
this.dispatchEvent(new CustomEvent('password', { detail: password }));
this.requestUpdate();
}}
>
</foxy-sign-in-form>
Expand Down
Loading

0 comments on commit cc06da6

Please sign in to comment.