Skip to content

Add reset password URL support to cl-identity-link component #108

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"email": "marco.montalbano@commercelayer.io"
},
"devDependencies": {
"@babel/core": "^7.27.1",
"@babel/core": "^7.27.4",
"@babel/preset-env": "^7.27.2",
"@babel/preset-react": "^7.27.1",
"@babel/preset-typescript": "^7.27.1",
Expand Down Expand Up @@ -45,7 +45,7 @@
"react-dom": "^19",
"react-syntax-highlighter": "^15.6.1",
"remark-gfm": "^4.0.1",
"sass": "^1.89.0",
"sass": "^1.89.2",
"storybook": "^8.6.14",
"typescript": "^5.8.3",
"webpack": "^5.99.9"
Expand Down
1 change: 1 addition & 0 deletions packages/docs/stories/identity/cl-identity-link.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const Template: StoryFn<Args> = (args) => {
type=${args.type ?? nothing}
target=${args.target ?? nothing}
scope=${args.scope ?? nothing}
reset-password-url=${args["reset-password-url"] ?? nothing}
>
Identity link
</cl-identity-link>
Expand Down
12 changes: 6 additions & 6 deletions packages/drop-in/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"dependencies": {
"@commercelayer/js-auth": "^6.7.2",
"@commercelayer/organization-config": "^2.3.0",
"@commercelayer/sdk": "^6.42.0",
"@commercelayer/sdk": "^6.43.0",
"@types/lodash": "^4.17.17",
"iframe-resizer": "4.3.11",
"js-cookie": "^3.0.5",
Expand All @@ -58,18 +58,18 @@
"@stencil/core": "^4.0.0"
},
"devDependencies": {
"@stencil/core": "^4.31.0",
"@stencil/core": "^4.35.0",
"@stencil/sass": "^3.2.1",
"@types/iframe-resizer": "^4.0.0",
"@types/jest": "^29.5.14",
"@types/js-cookie": "^3.0.6",
"@types/node": "^20.17.50",
"@types/node": "^20.19.1",
"jest": "^29.7.0",
"jest-cli": "^29.7.0",
"nodemon": "^3.1.10",
"puppeteer": "24.9.0",
"sass": "^1.89.0",
"ts-jest": "^29.3.4",
"puppeteer": "24.10.1",
"sass": "^1.89.2",
"ts-jest": "^29.4.0",
"typescript": "^5.8.3"
},
"license": "MIT",
Expand Down
3 changes: 2 additions & 1 deletion packages/drop-in/src/apis/commercelayer/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export async function getMyAccountUrl(): Promise<string | undefined> {
export async function getIdentityUrl(
type: "login" | "signup" | "logout",
scope?: string,
resetPasswordUrl?: string,
): Promise<string | undefined> {
const config = getConfig()

Expand All @@ -31,5 +32,5 @@ export async function getIdentityUrl(

return `${organizationConfig.links.identity}/${type}?clientId=${
config.clientId
}&scope=${scope ?? config.scope}&publicScope=${config.scope}&returnUrl=${getClosestLocationHref()}${lang != null ? `&lang=${lang}` : ""}`
}&scope=${scope ?? config.scope}&publicScope=${config.scope}&returnUrl=${getClosestLocationHref()}${lang != null ? `&lang=${lang}` : ""}${resetPasswordUrl != null ? `&resetPasswordUrl=${resetPasswordUrl}` : ""}`
}
26 changes: 26 additions & 0 deletions packages/drop-in/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export namespace Components {
"kind"?: "sku" | "bundle";
/**
* The rule used to determine the information that will be displayed. `cheapest` is the delivery lead time associated with the lower shipping method cost, `fastest` is the delivery lead time associated with the lower average time to delivery.
* @default "cheapest"
*/
"rule": "cheapest" | "fastest";
}
Expand Down Expand Up @@ -73,10 +74,12 @@ export namespace Components {
interface ClCart {
/**
* Indicate whether the minicart is open or not (available _only_ when the `cl-cart` component is used as _minicart_).
* @default false
*/
"open": boolean;
/**
* If `true` the minicart automatically opens as soon as an item is added to the shopping cart (available _only_ when the `cl-cart` component is used as _minicart_).
* @default false
*/
"openOnAdd": boolean;
/**
Expand All @@ -87,28 +90,36 @@ export namespace Components {
interface ClCartCount {
/**
* Toggle this switch to hide the counter when the cart is empty instead of showing `0`.
* @default false
*/
"hideWhenEmpty": boolean;
}
interface ClCartLink {
/**
* The browsing context in which to open the linked URL (a tab, a window, or an &lt;iframe&gt;).
* @default "_self"
*/
"target": "_self" | "_blank" | "_parent" | "_top";
}
interface ClCheckoutLink {
/**
* The browsing context in which to open the linked URL (a tab, a window, or an &lt;iframe&gt;).
* @default "_self"
*/
"target": "_self" | "_blank" | "_parent" | "_top";
}
interface ClIdentityLink {
/**
* Enable a custom reset password link visible on the login form page. When set, a "Forgot password?" link will be shown on the right below the Password field.
*/
"resetPasswordUrl"?: string;
/**
* Your sales channel [scope](https://docs.commercelayer.io/core/authentication#authorization-scopes) (used to restrict the dataset of your application to a market and/or stock location). If specified, it will override the default scope set in the drop-in library configuration. Otherwise, the default scope taken from the drop-in library configuration will be used.
*/
"scope"?: string;
/**
* The browsing context in which to open the linked URL (a tab, a window, or an &lt;iframe&gt;).
* @default "_self"
*/
"target": "_self" | "_blank" | "_parent" | "_top";
/**
Expand All @@ -125,6 +136,7 @@ export namespace Components {
interface ClMyAccountLink {
/**
* The browsing context in which to open the linked URL (a tab, a window, or an &lt;iframe&gt;).
* @default "_self"
*/
"target": "_self" | "_blank" | "_parent" | "_top";
}
Expand All @@ -142,6 +154,7 @@ export namespace Components {
interface ClPriceAmount {
/**
* The type of price amount to be displayed.
* @default "price"
*/
"type": "price" | "compare-at";
}
Expand Down Expand Up @@ -282,6 +295,7 @@ declare namespace LocalJSX {
"kind"?: "sku" | "bundle";
/**
* The rule used to determine the information that will be displayed. `cheapest` is the delivery lead time associated with the lower shipping method cost, `fastest` is the delivery lead time associated with the lower average time to delivery.
* @default "cheapest"
*/
"rule"?: "cheapest" | "fastest";
}
Expand Down Expand Up @@ -309,10 +323,12 @@ declare namespace LocalJSX {
interface ClCart {
/**
* Indicate whether the minicart is open or not (available _only_ when the `cl-cart` component is used as _minicart_).
* @default false
*/
"open"?: boolean;
/**
* If `true` the minicart automatically opens as soon as an item is added to the shopping cart (available _only_ when the `cl-cart` component is used as _minicart_).
* @default false
*/
"openOnAdd"?: boolean;
/**
Expand All @@ -323,28 +339,36 @@ declare namespace LocalJSX {
interface ClCartCount {
/**
* Toggle this switch to hide the counter when the cart is empty instead of showing `0`.
* @default false
*/
"hideWhenEmpty"?: boolean;
}
interface ClCartLink {
/**
* The browsing context in which to open the linked URL (a tab, a window, or an &lt;iframe&gt;).
* @default "_self"
*/
"target"?: "_self" | "_blank" | "_parent" | "_top";
}
interface ClCheckoutLink {
/**
* The browsing context in which to open the linked URL (a tab, a window, or an &lt;iframe&gt;).
* @default "_self"
*/
"target"?: "_self" | "_blank" | "_parent" | "_top";
}
interface ClIdentityLink {
/**
* Enable a custom reset password link visible on the login form page. When set, a "Forgot password?" link will be shown on the right below the Password field.
*/
"resetPasswordUrl"?: string;
/**
* Your sales channel [scope](https://docs.commercelayer.io/core/authentication#authorization-scopes) (used to restrict the dataset of your application to a market and/or stock location). If specified, it will override the default scope set in the drop-in library configuration. Otherwise, the default scope taken from the drop-in library configuration will be used.
*/
"scope"?: string;
/**
* The browsing context in which to open the linked URL (a tab, a window, or an &lt;iframe&gt;).
* @default "_self"
*/
"target"?: "_self" | "_blank" | "_parent" | "_top";
/**
Expand All @@ -361,6 +385,7 @@ declare namespace LocalJSX {
interface ClMyAccountLink {
/**
* The browsing context in which to open the linked URL (a tab, a window, or an &lt;iframe&gt;).
* @default "_self"
*/
"target"?: "_self" | "_blank" | "_parent" | "_top";
}
Expand All @@ -378,6 +403,7 @@ declare namespace LocalJSX {
interface ClPriceAmount {
/**
* The type of price amount to be displayed.
* @default "price"
*/
"type"?: "price" | "compare-at";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,26 @@ describe("cl-identity-link.spec", () => {
expect(log).not.toHaveBeenCalled()
})

it('renders the identity link with the resetPasswordUrl when type="login"', async () => {
const { root, waitForChanges } = await newSpecPage({
components: [ClIdentityLink],
html: '<cl-identity-link type="login" reset-password-url="https://example.com/reset-password">Login</cl-identity-link>',
})

await waitForChanges()

expect(root).toEqualHtml(`
<cl-identity-link type="login" target="_self" reset-password-url="https://example.com/reset-password">
<a href="https://drop-in-js.commercelayer.app/identity/login?clientId=kuSKPbeKbU9LG9LjndzieKWRcfiXFuEfO0OYHXKH9J8&amp;scope=market:code:usa&amp;publicScope=market:code:usa&amp;returnUrl=http://testing.stenciljs.com/&amp;lang=en&amp;resetPasswordUrl=https://example.com/reset-password" target="_self">
Login
</a>
</cl-identity-link>
`)

expect(log).toHaveBeenCalledTimes(0)
expect(log).not.toHaveBeenCalled()
})

it('renders the identity link when type="signup"', async () => {
const { root, waitForChanges } = await newSpecPage({
components: [ClIdentityLink],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ export class ClIdentityLink {
*/
@Prop({ reflect: true }) type!: "login" | "signup" | "logout" | undefined

/**
* Enable a custom reset password link visible on the login form page.
* When set, a "Forgot password?" link will be shown on the right below the Password field.
*/
@Prop({ reflect: true }) resetPasswordUrl?: string

/**
* Your sales channel [scope](https://docs.commercelayer.io/core/authentication#authorization-scopes)
* (used to restrict the dataset of your application to a market and/or stock location).
Expand All @@ -57,7 +63,7 @@ export class ClIdentityLink {

private async updateUrl(type: typeof this.type): Promise<void> {
this.href = isValidUnion(type, this.typeList)
? await getIdentityUrl(type, this.scope)
? await getIdentityUrl(type, this.scope, this.resetPasswordUrl)
: undefined

logUnion(this.host, "type", type, this.typeList)
Expand Down
11 changes: 6 additions & 5 deletions packages/drop-in/src/components/cl-identity-link/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@

## Properties

| Property | Attribute | Description | Type | Default |
| ------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | ----------- |
| `scope` | `scope` | Your sales channel [scope](https://docs.commercelayer.io/core/authentication#authorization-scopes) (used to restrict the dataset of your application to a market and/or stock location). If specified, it will override the default scope set in the drop-in library configuration. Otherwise, the default scope taken from the drop-in library configuration will be used. | `string \| undefined` | `undefined` |
| `target` | `target` | The browsing context in which to open the linked URL (a tab, a window, or an &lt;iframe&gt;). | `"_blank" \| "_parent" \| "_self" \| "_top"` | `"_self"` |
| `type` _(required)_ | `type` | The user account access action. | `"login" \| "logout" \| "signup" \| undefined` | `undefined` |
| Property | Attribute | Description | Type | Default |
| ------------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | ----------- |
| `resetPasswordUrl` | `reset-password-url` | Enable a custom reset password link visible on the login form page. When set, a "Forgot password?" link will be shown on the right below the Password field. | `string \| undefined` | `undefined` |
| `scope` | `scope` | Your sales channel [scope](https://docs.commercelayer.io/core/authentication#authorization-scopes) (used to restrict the dataset of your application to a market and/or stock location). If specified, it will override the default scope set in the drop-in library configuration. Otherwise, the default scope taken from the drop-in library configuration will be used. | `string \| undefined` | `undefined` |
| `target` | `target` | The browsing context in which to open the linked URL (a tab, a window, or an &lt;iframe&gt;). | `"_blank" \| "_parent" \| "_self" \| "_top"` | `"_self"` |
| `type` _(required)_ | `type` | The user account access action. | `"login" \| "logout" \| "signup" \| undefined` | `undefined` |


----------------------------------------------
Expand Down
Loading