@spscommerce/items-experience
v1.4.1
Published
The Items Experience is a collection of UI components that interact with Assortment's Items API. Using these components in multiple frontend projects will ensure a consistent user experience across SPS applications.
Downloads
473
Readme
Items Experience
The Items Experience is a collection of UI components that interact with Assortment's Items API. Using these components in multiple frontend projects will ensure a consistent user experience across SPS applications.
"Earthshaking"
-- @omnipitous
Contents
Installation
Install the package:
pnpm add @spscommerce/items-experience
The package is hosted publicly on the NPM registry, so no extra configuration or permissions are required for installing.
This package requires some Peer Dependencies that should also be installed (refer to package.json
for versions):
@sps-woodland/content-row
@sps-woodland/core
@sps-woodland/file-upload
@sps-woodland/illustrations
@sps-woodland/key-value-set
@sps-woodland/page-title
@sps-woodland/product-bar
@sps-woodland/tokens
@sps-woodland/zero-state
@spscommerce/ds-react
@spscommerce/utils
@spscommerce/asst-api
@spscommerce/form-presets-api
@spscommerce/items-api
react
react-dom
react-query
react-router-dom
(optional)
Basic Setup
Items Experience was built to be used with React Router (version 6.4 or later), but is compatible with any routing setup.
All routing options require some initial basic setup, which includes:
- Setting up a context provider
- Setting up API providers
Routing can be setup after the basic setup.
Context Provider
All components from Items Experience must exist within the context of ItemsExperienceBasicContextProvider
.
ItemsExperienceBasicContextProvider
provides common data and functions from the host app:
type ItemsExperienceBasicContextProps = {
/**
* Provided by `useGrowlers` from `@spscommerce/ds-react`
*/
createGrowler: (config: GrowlerConfig) => void;
/**
* Allows form-presets to be saved for your application
*/
formPresetDetails: {
appName: string;
advancedSearchDefaultsFormName: string;
selectedColumnsFormName: string;
};
/**
* Options for advanced search. Currently only includes standard attributes.
*/
advancedSearchOptions: {
/**
* An array of standard attributes that will be used in advanced search.
*/
standardAttributes: Attribute[];
};
/**
* Optional array of available attributes to be used in searches.
*/
availableAttributeList?: string[];
/**
* Config options for the Items Index page
*/
itemsIndexConfig: {
/**
* An array of unique attributes.
* Affects available columns.
*/
uniqueAttributes: string[];
/**
* Boolean flag to enable Stage Item feature.
* Affects column chooser.
*/
isStageItemEnabled: boolean;
/**
* Boolean flag to enable MSIS feature.
* Affects column chooser.
*/
isMsisUiEnabled: boolean;
/**
* Boolean flag to enable Media Item feature.
*/
enableMediaItem: boolean;
/**
* Optional boolean to show the "Add Item" button.
* Defaults to false.
*/
showAddItemsBtn?: boolean;
/**
* Array of columns that are initially selected.
*/
columnsInitiallySelected: ColumnType[];
/**
* Array of columns that are initially unselected.
*/
columnsInitiallyUnselected: ColumnType[];
/**
* Optional boolean to hide Status column.
* Defaults to false.
*/
hideStatusColumn?: boolean;
};
/**
* An optional {@link QueryClient} can be passed to Items Experience.
*
* Note: The {@link QueryCache} will be replaced.
*
* It is not recommended to pass a `QueryClient` to Items Experience unless greater control is required, such as disabling retries during testing.
* Items Experience will create its own `QueryClient`.
*/
queryClient?: QueryClient;
/**
* Your application, including Items Experience pages
*/
children: React.ReactNode;
};
API Provider Setup
In addition to context providers, Items Experience requires a valid user token to make network requests. All page components will wait until a valid token has been provided before rendering to ensure there are no race conditions.
Items Experience exports a function to set the token, updateApiProviders
:
import { updateApiProviders } from "@spscommerce/items-experience";
updateApiProviders(env, "user_token");
Draft Notification Setup
The forms in this package save drafts to mitigate the loss of unsaved work done by the end user.
Although optional, it is recommended that the user is notified when a draft is saved.
To do this, Items Experience exports triggerDraftNotifications()
which must be called after every rout change.
If you are using React-Router, setup may look like this:
import { useLocation } from "react-router-dom";
import { triggerDraftNotifications } from "@spscommerce/items-experience";
export function Component() {
// Get the current location from react-router
const location = useLocation();
// Call `triggerDraftNotifications` any time `location` changes
useEffect(() => {
triggerDraftNotifications();
}, [location]);
return ...
}
Routing
Items Experience consists of multiple pages, and therefore requires a routing solution. Simple routing configuration can be done with React Router, but Items Experience can also be configured to work with any router.
React Router
If your application uses React Router version 6.4, then setup can be much simpler. Items Experience exports a RouteObject
that can be added to your app's existing RouteObject
at any level. It uses relative routing to automatically configure navigation between pages.
ItemsExperienceBasicContextProvider
and updateApiProviders
are still required for setup, but ItemsExperienceUrlContextProvider
is not because routing is handled automatically.
import { itemsExperienceRouteObject } from "@spscommerce/items-experience/react-router";
const router = createHashRouter([
{
path: "/",
element: <Navigate to="a" />,
},
{
path: "/a",
element: <A />,
},
{
path: "/b",
element: <B />,
},
{
path: "/items",
element: <Outlet />,
children: [itemsExperienceRouteObject()],
},
]);
Optional parameters can be provided to itemsExperienceRouteObject
, which allow Items Experience to link to external pages:
{
errorsIndexLocation?: string;
errorsByItemLocation?: string;
imageSettingsLocation?: string;
exportsConfigLocation?: string;
}
Please see src/App.tsx
for a full example.
Using Your Own Router
This package exports each page as a single component so they can be used in your project's routing system. Each page component can exist under the URL of your choice within your project, but those URLs must be provided to ItemsExperienceUrlContextProps
to enable navigation between pages.
ItemsExperienceUrlContextProvider
provides routing data and functions from the host app. This component allows Items Experience components to integrate with your application's router:
type ItemsExperienceUrlContextProps = {
/**
* Your application, including Items Experience pages
*/
children: React.ReactNode;
/**
* URL data and functions.
*/
value: {
/**
* Path parameters for the current route
*/
urlParams: Record<string, string | undefined>;
/**
* Search parameters for the current route
*/
urlSearchParams: URLSearchParams;
/**
* Function to set search parameters. Used for url state.
* @param params Search parameters to set
* @param replace Should the new parameters replace the current parameters (ie. not add to location history)
* @returns void
*/
setUrlSearchParams: (params: URLSearchParams, replace: boolean) => void;
/**
* A function to navigate to different url's
* @param location The location to navigate to
* @returns void
*/
navigateTo: (location: string) => void;
/**
* Object containing strings that specify the url location of each page when routing is handled by the host app.
* These locations allow Items Experience pages to link to each other.
*
* Required locations have a corresponding page component exported by Items Experience.
* For example:
* ```
* // Corresponds with `errorDetailsLocation`
* import { ErrorDetailsPage } from '@spscommerce/items-experience'
* ```
*
* Optional locations link to pages that are external to Items Experience.
* Typically, these will not be used by teams other than Assortment.
*/
locations: {
/**
* Errors Index location.
* This location is external to Items Experience and is optional.
*/
errorsIndexLocation?: string;
/**
* Error Details location.
*/
errorDetailsLocation: string;
/**
* Errors by Item location.
* This location is external to Items Experience and is optional.
*/
errorsByItemLocation?: string;
/**
* Imports Index location.
*/
importsIndexLocation: string;
/**
* Imports Create Template location.
*/
importsCreateTemplateLocation: string;
/**
* Items Index location.
*/
itemsIndexLocation: string;
/**
* Item Details location.
*/
itemDetailsLocation: string;
/**
* Advanced Search Defaults location.
*/
advSearchDefaultsLocation: string;
/**
* Image Settings location.
* This location is external to Items Experience and is optional.
*/
imageSettingsLocation?: string;
/**
* Exports Configuration location.
* This location is external to Items Experience and is optional.
*/
exportsConfigLocation?: string;
};
};
};
Items Experience exports the following page components in each category:
- Preferences
AdvancedSearchDefaultsPage
- Errors
ErrorDetailsPage
- Imports
ImportsCreateTemplatePage
ImportsIndexPage
- Items
ItemsIndexPage
ItemDetailsPage
Some pages expect URL path parameters (ItemsExperienceUrlContextProps.value.urlParams
) and are required to function as expected:
ErrorDetailsPage
expectsitemInfoId
- ex.
errors/detail/:itemInfoId
- ex.
ItemsDetail
expectsid
- ex.
items/detail/:id
- ex.
When Items Experience pages link to each other, these path parameters will be automatically be appended to the locations provided in ItemsExperienceUrlContextProps.value.locations
.
The host app needs to provide the path parameters to ItemsExperienceUrlContextProvider
because Items Experience components do not have direct access to the router.
Contributing
When contributing, please keep in mind that this is a public package that is used by multiple teams. Breaking changes need to be documented (see Pull Requests and Deployment for details).
Installation
This project is part of the Assortment UI workspace and uses pnpm
for package management.
Install dependencies:
pnpm i
Project Structure
lib
The lib
folder contains the code that will be exported. It has two sub-folders: core
and react-router
. These sub-folder correspond to the two entrypoints of the package.
core
contains the base components the Items Experience, without any routing.react-router
contains the react-router adapter, which is aRouteObject
that can be dropped into an existing react router config
src
The src
folder contains an example application that uses the Items Experience with the react-router adapter.
Running the example project:
pnpm dev
Testing
This project is set up for unit tests and end-to-end tests.
Unit Tests
This project uses Vitest with React Testing Library for unit tests. These tests will have a *.test.(ts|tsx)
file extension.
Running tests in watch mode:
pnpm test
Running tests with an explorer UI:
pnpm test:ui
End-to-end Tests
This project uses Playwright for end-to-end tests. These tests will exist under the e2e
folder and will have a *.spec.ts
file extension.
Running tests:
pnpm test:e2e
Playwright also features a UI for step by step debugging.
Running the Playwright UI:
pnpm test:e2e-ui
Storybook
This project uses Storybook to help with development.
Running Storybook:
pnpm storybook
Pull Requests and Deployment
This projects uses Microsoft's beachball to manage semantic versioning.
Pull Requests
When opening a pull request, the build pipeline will check if a changefile is included. The build will fail if no changefile is found. To create a changefile, run the beachball:change
script. The cli will ask a few questions and then generate a changefile.
When generating a changefile, the type of change must be specified:
- Patch - bug fixes; no backwards incompatible changes.
- Minor - small feature; backwards compatible changes.
- None - this change does not affect the published package in any way.
- Major - major feature; breaking changes.
Deployment
The project is not automatically deployed when a Pull Request is merged. The project must be deployed manually by running the Items Experience Publish pipeline.
When this pipeline is run, it will run the following on the main
branch:
- check lint
- check formatting
- run tests
- build
- check for changefiles
- increment package version, delete changefiles, commit to main
- publish the package to NPM
Rotating NPM keys
The key required to publish the package can only be valid for a maximum of 365 days. When the key expires, it must be replaced with a newly generated key. A newly key can be generated on NPM, using the spsc_asst account.
Scripts
dev
: run the example projecttest
: run unit tests in watch modetest:ui
: run unit tests with the explorer UItest:ci
: run unit tests in CItest:e2e
: run the playwright e2e teststest:e2e-ui
: run the playwright e2e test explorer and debuggerlint:lib
: lint the lib folderlint:src
: lint the src folderprettier:check
: check that all files are formatted correctlystorybook
: run storybookbuild-storybook
: build deployable storybook files (not necessary to run storybook locally)beachball:change
: generate a changefile (at least one changefile is required for a pull request)beachball:check
: check that a changefile accompanies changes that are not in main (used by CI)beachball:publish
: publish the package (used by CI)