Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@thoughtindustries/catalog

Package Overview
Dependencies
Maintainers
1
Versions
23
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@thoughtindustries/catalog - npm Package Compare versions

Comparing version 1.2.3 to 1.2.4

src/catalog-loader.tsx

14

package.json
{
"name": "@thoughtindustries/catalog",
"version": "1.2.3",
"version": "1.2.4",
"description": "A base component for catalog",

@@ -25,13 +25,13 @@ "author": "Lu Jiang <lu.jiang@thoughtindustries.com>",

"@apollo/client": "^3.7.0",
"@thoughtindustries/content": "^1.2.3",
"@thoughtindustries/content": "^1.2.4",
"@thoughtindustries/header": "^1.1.2",
"@thoughtindustries/hooks": "^1.2.2",
"@thoughtindustries/pagination": "^1.2.2",
"clsx": "^1.1.1",
"@thoughtindustries/pagination": "^1.2.3",
"dayjs": "^1.10.8",
"graphql": "^16.6.0",
"i18next": "^21.10.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react": "^17 || ^18",
"react-dom": "^17 || ^18",
"react-i18next": "^11.18.6",
"tailwind-merge": "^1.14.0",
"use-debounce": "^7.0.1"

@@ -43,3 +43,3 @@ },

},
"gitHead": "55420e262db95eb8b0d6a7d223dcf271c5035773"
"gitHead": "a55fe18b2076468826ae5c4cbea0ca8802e31e10"
}

@@ -5,51 +5,217 @@ # `@thoughtindustries/catalog`

## Import component
**Table of contents:**
- [`Catalog`](#catalog)
- [Core catalog components](#core-catalog-components)
- [`CatalogLinkButton`](#cataloglinkbutton)
- [`CatalogProvider`](#catalogprovider)
- [`HeightEqualizer`](#heightequalizer)
- [Core catalog hooks](#core-catalog-hooks)
- [`useCatalog`](#usecatalog)
## Catalog
The `Catalog` component takes a callback function to handle catalog item's add-to-queue event, and optional properties to customize the catalog like price formatting, currency formating, etc. It must be a descendent of the `CatalogProvider` component.
### Example code
```tsx
import { CatalogProvider, Catalog } from '@thoughtindustries/catalog';
export function MyComponent() {
// ...
const addToQueueHandler = (item) => Promise.resolve();
return (
<CatalogProvider ssr pathName="/catalog">
<Catalog onAddedToQueue={addToQueueHandler} />
</CatalogProvider>
);
}
```
import { Catalog, CatalogProvider } from '@thoughtindustries/catalog';
## Props
| Name | Required | Type | Description |
| -------- | -------- | ----------------------------- | ------------------------- |
| title | No | <code>string</code> | The title that appears on top of the catalog. |
| alternateTitleDisplay | No | <code>boolean</code> | Option to display the alternative title. |
| pagination | No | <code>(args: PaginationFnArgs) => ReactElement</code> | An alternative view for the pagination display. |
| companyHasSessionLevelCustomFieldsFeature | No | <code>boolean</code> | Company feature flag for content hydration. |
| companyTimeZone | No | <code>string</code> | Company property to override item's timezone. |
| onAddedToQueue | Yes | <code>(item: CatalogResultItem) => Promise<boolean \| void></code> | Event handler for add to queue button for each item. |
| onClick | No | <code>(evt: SyntheticEvent, item: CatalogResultItem) => void</code> | Optional click event handler for each item. |
| priceFormat | No | <code>(priceInCents: number) => string</code> | A callback that is invoked to format monetary value with currency. It takes a number value for the price in cent unit and return the formatted value. Default value uses `Intl.NumberFormat` with props `companyDefaultLocale` and/or `currencyCode` to enable locale-specific currency formatting. |
| companyDefaultLocale | No | <code>string</code> | A locale value to format price when prop `priceFormat` is not specified. Used to speficy the locale in `Intl.NumberFormat`. Default to `en-US`. |
| currencyCode | No | <code>string</code> | A currency code value to format price when prop `priceFormat` is not specified. Used to speficy the currency code in `Intl.NumberFormat`. Default to `USD`. |
| numberOfContentItems | No | <code>number</code> | Specify the number of items to display in the `grid` view. |
## Core catalog components
Core catalog components are objects that contain all of business logic for the catalog concept that they represent. They're used to parse and process data.
### CatalogLinkButton
The `CatalogLinkButton` component renders a link button that conditionally overrides the link behavior to handle client side navigation. It must be a descendent of a `CatalogProvider` component. This is used internally by `Catalog` component to handle both server and client side rendering. If you are composing own version of `Catalog` component for different layout and styles, this component can be useful.
#### Example code
```tsx
import { CatalogProvider, CatalogLinkButton } from '@thoughtindustries/catalog';
function CustomCatalog() {
// ...
return (
<>
{/* // ... */}
<CatalogLinkButton href="/catalog?page=2" />
</>
);
}
export function MyComponent() {
// ...
return (
<CatalogProvider ssr={false} pathName="/catalog">
<CustomCatalog />
</CatalogProvider>
);
}
```
## Usage
#### Props
This component is inherently an `HTMLAnchorElement` element with only the `onClick` prop omitted. The component will use own `onClick` handler.
#### Related components
- [`CatalogProvider`](#catalogprovider)
#### Related hooks
- [`useCatalog`](#usecatalog)
### CatalogProvider
The `CatalogProvider` component creates a context for using a catalog. It requires an url path name and a `ssr` flag to handle the data fetching as well as user interactions with catalog filters. It creates relevant context values that can be accessed by any descendent component using the `useCatalog` hook.
You must use this component if you want to use the `useCatalog` hook, or if you would like to use the `CatalogLinkButton` component.
#### Example code
```tsx
import { CatalogProvider } from '@thoughtindustries/catalog';
export function App() {
return <CatalogProvider ssr pathName="/catalog">{/* Your JSX */}</CatalogProvider>;
}
```
# CatalogProvider takes a config object to parse URL search params and/or handle custom catalog configurations
# Sample config object
const config = {
parsedUrl: {
pathname: '/catalog',
searchString: '?query=test'
}
#### Props
| Name | Required | Type | Description |
| ---------------------- | -------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| children | Yes | <code>React.ReactNode</code> | Any `ReactNode` elements. |
| pathName | Yes | <code>string</code> | The URL path for the catalog. |
| searchString | No | <code>string</code> | The URL search string used to initialize the catalog. |
| layoutId | No | <code>string</code> | The id of the custom catalog widget layout. Use this along with `widgetId` to specify the custom catalog widget created in the core platform. |
| widgetId | No | <code>string</code> | The id of the custom catalog widget. Use this along with `layoutId` to specify the custom catalog widget created in the core platform. |
| ssr | Yes | <code>boolean</code> | Option to render the catalog on the server side or client side. When set to `true`, catalog will be rendered on the server side and user interactions with the filters will cause a full page load. When set to `false`, catalog will be rendered on the client side and user interactions with the filters will only affect the catalog portion of the page. |
#### Related hooks
- [`useCatalog`](#usecatalog)
### HeightEqualizer
The `HeightEqualizer` component is a wrapper component that calculate the maximum height from its children `HeightEqualizerElement` components. With the max height value, it updates all children `HeightEqualizerElement` components to the same value. This is used internally by `Catalog` component to handle catalog item height in `grid` view. If you are composing own version of `Catalog` component for different layout and styles, this component can be useful.
#### Example code
```tsx
import { HeightEqualizer, HeightEqualizerElement } from '@thoughtindustries/catalog';
export function MyComponent() {
// ...
return (
<HeightEqualizer>
<>
<HeightEqualizerElement key="item-1">{/* Your JSX */}</HeightEqualizerElement>
<HeightEqualizerElement key="item-2">{/* Your JSX */}</HeightEqualizerElement>
</>
</HeightEqualizer>
);
}
# Sample config object with custom catalog configurations
const config = {
parsedUrl: {
pathname: '/catalog',
searchString: '?query=test'
},
```
#### Props
##### Props for HeightEqualizer
| Name | Required | Type | Description |
| ----------- | -------- | ---------------------- | ---------------------- |
| children | Yes | <code>ReactNode</code> | A `ReactNode` element. |
| timeout | No | <code>number</code> | Time to recalculate heights. |
| animationSpeed | No | <code>number</code> | Time of animation for height change (in milliseconds). |
##### Props for HeightEqualizerElement
| Name | Required | Type | Description |
| ----------- | -------- | ---------------------- | ---------------------- |
| children | No | <code>ReactNode</code> | A `ReactNode` element. |
| name | Yes | <code>string</code> | All heights of elements with the same name will be compared. |
| as | No | <code>string</code> | An HTML tag to be rendered as the base element wrapper. The default is `div`. |
| className | No | <code>string</code> | A string of styling class names to apply to the underlying element. |
## Core catalog hooks
Core catalog hooks are functions that allow you to use state and other methods inside catalog components.
### useCatalog
The `useCatalog` hook provides access to the catalog context. It must be a descendent of a `CatalogProvider` component.
#### Example code
```tsx
import { CatalogProvider, useCatalog } from '@thoughtindustries/catalog';
export function MyComponent() {
return (
<CatalogProvider ssr={false} pathName="/catalog">
<CatalogLoader />
</CatalogProvider>
);
}
<CatalogProvider config={config}>
<Catalog onAddedToQueue={(item) => Promise.resolve()} />
</CatalogProvider>
export function CatalogLoader() {
const { isLoading } = useCatalog();
# Or use custom pagination
<CatalogProvider config={config}>
<Catalog
onAddedToQueue={(item) => Promise.resolve()}
pagination={({page, pageSize, total, getPageLink}) => <>...</>} />
</CatalogProvider>
if (isLoading) {
return <>Loading data</>;
}
# Or use custom price formatter
const priceFormat = (priceInCents) => {
const formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
});
return formatter.format(priceInCents / 100);
return ({/* Your JSX */});
}
<CatalogProvider config={config}>
<Catalog
onAddedToQueue={(item) => Promise.resolve()}
priceFormat={priceFormat} />
</CatalogProvider>
```
#### Return value
The `useCatalog` hook returns an object with the following keys:
| Name | Required | Description |
| ------------------------------- | -------- | ----------- |
| `params` | Yes | The catalog content and metadata. |
| `urlManager` | Yes | The URL manager instance to manage URL state. |
| `ssr` | Yes | This is pass-through value for the prop `ssr` of `CatalogProvider` component. |
| `navigateClientSideAsync` | No | An async function for user interaction with the catalog filters. This value is provided for client side render. |
| `isLoading` | Yes | The loading state of data fetching. During server side render, this value is always `false`. During client side render, this value will reflect the data fetching loading state. |
| `scrollToRef` | No | A reference to `HTMLDivElement`. During client side render, browser will scroll to the top of the assigned element when fetching data. |
| `contentWrapperRef` | No | A reference to `HTMLDivElement`. During client side render, browser will mutate the height of the assigned element when fetching data. This will prevent flashing effect when user interface switches between catalog content and a loading indicator. |
#### Related components
- [`CatalogProvider`](#catalogprovider)
import { CatalogParams } from '../../utilities/parse-catalog-data';
import { CatalogParsedURL, CatalogURLManager } from '../../utilities/manage-catalog-url';
import { ReactNode } from 'react';
import { ReactNode, RefObject } from 'react';
export type navigateClientSideAsyncFnParams = {
url: string;
pushToUrl?: boolean;
};
export type CatalogContextType = {
params: CatalogParams;
urlManager: CatalogURLManager;
ssr: boolean;
navigateClientSideAsync?: (params: navigateClientSideAsyncFnParams) => Promise<void>;
isLoading: boolean;
scrollToRef?: RefObject<HTMLDivElement>;
contentWrapperRef?: RefObject<HTMLDivElement>;
};
export type CatalogProviderConfig = {
parsedUrl: CatalogParsedURL;
};
export type CatalogProviderProps = {
config: CatalogProviderConfig;
export type CatalogProviderProps = CatalogParsedURL & {
children: ReactNode;
layoutId?: string;
widgetId?: string;
ssr: boolean;
};

@@ -35,4 +35,4 @@ import { Dispatch, ReactNode, SetStateAction } from 'react';

as?: string;
/** A string of classes to apply to the `div` that wraps the Shop Pay button. */
/** A string of styling class names to apply to the underlying element. */
className?: string;
}

@@ -0,2 +1,3 @@

export * from './catalog-link-button';
export * from './catalog-provider';
export * from './height-equalizer';

@@ -7,3 +7,4 @@ import { GlobalTypes } from '@thoughtindustries/content';

Sort,
serializeSort
serializeSort,
CatalogRequestParams
} from '../parse-catalog-data';

@@ -30,5 +31,5 @@ import {

export default class CatalogURLManager {
private readonly _pathname;
private readonly _searchParams;
private readonly _parsedRequestParams;
private _pathname = '';
private _searchParams: URLSearchParams | undefined;
private _parsedRequestParams: Partial<CatalogRequestParams> = {};
private _isCurated: boolean | undefined;

@@ -38,4 +39,17 @@ private _selectedDisplayType: GlobalTypes.ContentItemDisplayType | undefined;

constructor(parsedUrl: CatalogParsedURL) {
const { pathname, searchString } = parsedUrl;
this._pathname = pathname;
this._parseUrl(parsedUrl);
}
updateUrl(url: string) {
const { pathname, search } = new URL(url, 'http://dummy.com');
const parsedUrl = {
pathName: pathname,
searchString: search
};
this._parseUrl(parsedUrl);
}
private _parseUrl(parsedUrl: CatalogParsedURL) {
const { pathName, searchString } = parsedUrl;
this._pathname = pathName;
this._searchParams = new URLSearchParams(searchString || undefined);

@@ -192,6 +206,12 @@ this._parsedRequestParams = toRequestParams(this._searchParams);

composeURLForSetSearchTermForm(): string {
composeActionURLForSetSearchTermForm(): string {
return this._composeURL('');
}
composeURLForSetSearchTermForm(searchTerm: string): string {
const clonedParams = this._resetOrDefaultClonedParams();
clonedParams.set(CatalogURLSearchParams.SearchTerm, searchTerm);
return this._composeURL(clonedParams.toString());
}
composeSearchTermFormHiddenFields(): SearchTermFormHiddenField[] {

@@ -198,0 +218,0 @@ const clonedParams = this._resetOrDefaultClonedParams();

import { AggregationFilter } from '../parse-catalog-data';
export type CatalogParsedURL = {
pathname: string;
pathName: string;
searchString?: string;

@@ -6,0 +6,0 @@ };

@@ -25,4 +25,3 @@ import { CatalogParams } from './types';

// default general params
isLoading: false,
pageSize: DEFAULT_PAGE_SIZE
};

@@ -56,3 +56,2 @@ import { GlobalTypes } from '@thoughtindustries/content';

error?: string;
isLoading: boolean;
pageSize: number;

@@ -59,0 +58,0 @@ };

@@ -1,2 +0,2 @@

import { SyntheticEvent, ReactElement } from 'react';
import { SyntheticEvent, ReactElement, ElementType } from 'react';
import { HydratedContentItem, GlobalTypes } from '@thoughtindustries/content';

@@ -13,7 +13,7 @@

companyHasSessionLevelCustomFieldsFeature?: boolean;
/** company property to override item timezone */
/** company property to override item's timezone */
companyTimeZone?: string;
/** event handler for add to queue button */
/** event handler for add to queue button for each item */
onAddedToQueue: (item: CatalogResultItem) => Promise<boolean | void>;
/** optional event handler for result item */
/** optional event handler for each item */
onClick?: (evt: SyntheticEvent, item: CatalogResultItem) => void;

@@ -35,2 +35,3 @@ /** optional function for prioritized price formatting */

getPageLink: (page: number) => string;
linkComponent?: ElementType;
};

@@ -40,3 +41,3 @@ export type PaginationFn = (args: PaginationFnArgs) => ReactElement;

export interface CatalogProps extends CatalogResultsProps {
/** title that appears on top of the link lists */
/** title that appears on top of the catalog */
title?: string;

@@ -43,0 +44,0 @@ /** display alternate title */

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc