diff --git a/packages/react/src/ShopifyCookies.tsx b/packages/react/src/ShopifyCookies.tsx index 3fd9c3f1..0a5764ab 100644 --- a/packages/react/src/ShopifyCookies.tsx +++ b/packages/react/src/ShopifyCookies.tsx @@ -16,6 +16,17 @@ interface ShopifyCookiesProps { hasUserConsent?: boolean; } +/** + * Set user and session cookies and refresh the expiry time + * @param domain - The domain scope of the cookie + * @param hasUserConsent - Defaults to false. If hasUserConsent is true, we can set Shopify unique user token cookie + * @example + * ```tsx + * import {ShopifyCookies} from '@shopify/hydrogen-react'; + * + * + * ``` + */ export function ShopifyCookies(props: ShopifyCookiesProps) { const {domain = '', hasUserConsent = false} = props; diff --git a/packages/react/src/analytics-utils.ts b/packages/react/src/analytics-utils.ts index 7abb3899..f9b48cff 100644 --- a/packages/react/src/analytics-utils.ts +++ b/packages/react/src/analytics-utils.ts @@ -26,25 +26,40 @@ export function schemaWrapper(schemaId: string, payload: object) { * Parses global id (gid) and returns the resource type and id. * @see https://shopify.dev/api/usage/gids * @param gid - A shopify GID (string) - * @returns \{ id: number, resource: string \} + * @returns \{ id: string | number | null, resource: string| null \} * * @example * ```ts * const {id, resource} = parseGid('gid://shopify/Order/123') * // => id = 123, resource = 'Order' + * + * * const {id, resource} = parseGid('gid://shopify/Cart/abc123') + * // => id = "abc123", resource = 'Cart' * ``` **/ export function parseGid(gid: string | undefined): { - id: number | null; + id: string | number | null; resource: string | null; } { - if (typeof gid !== 'string') return {id: null, resource: null}; - const matches = gid.match(/^gid:\/\/.hopify\/(\w+)\/(\d+)/); + const defaultReturn = {id: null, resource: null}; + + if (typeof gid !== 'string') { + return defaultReturn; + } + + // TODO: add support for parsing query parameters on complex gids + const matches = gid.match(/^gid:\/\/.hopify\/(\w+)\/([a-z0-9]+)/); + if (!matches || matches.length === 1) { - return {id: null, resource: null}; + return defaultReturn; + } + const id = matches[2] ?? null; + const resource = matches[1] ?? null; + + // if id is of only numbers, return as an integer + if (id && /^\d+$/.test(id)) { + return {id: parseInt(id, 10), resource}; } - const id = matches[2] ? parseInt(matches[2], 10) : null; - const resource = matches[1] ? matches[1] : null; return {id, resource}; } diff --git a/packages/react/src/cookie-utils.test.ts b/packages/react/src/cookie-utils.test.ts new file mode 100644 index 00000000..e3a8b345 --- /dev/null +++ b/packages/react/src/cookie-utils.test.ts @@ -0,0 +1,31 @@ +import { + buildUUID, + hexTime, + getShopifyCookies, + ShopifyCookies, +} from './cookies-utils.js'; + +describe('cookies-utils', () => { + describe('buildUUID', () => { + it('returns a string', () => { + expect(typeof buildUUID()).toBe('string'); + }); + }); + + describe('hexTime', () => { + it('returns a string', () => { + expect(typeof hexTime()).toBe('string'); + }); + }); + + describe('getShopifyCookies', () => { + it('returns object with SHOPIFY_Y and SHOPIFY_X', () => { + const cookie = + '_shopify_m=persistent; _y=44c60bb0-577c-4901-874c-92cb323fccf1; _shopify_y=44c60bb0-577c-4901-874c-92cb323fccf1; _shopify_y=44c60bb0-577c-4901-874c-92cb323fccf1; _tracking_consent={"lim":["GDPR"],"v":"2.0","con":{"GDPR":""},"reg":"CCPA"}; _shopify_s=a797b9ef-C0E7-4536-18BA-2828BA504882'; + expect(getShopifyCookies(cookie)).toMatchObject({ + _shopify_y: '44c60bb0-577c-4901-874c-92cb323fccf1', + _shopify_s: 'a797b9ef-C0E7-4536-18BA-2828BA504882', + }); + }); + }); +});