smartsell-ui-kit
v0.1.6
Published
Public UI primitives and modal helpers for SmartSell applications.
Maintainers
Readme
smartsell-ui-kit
smartsell-ui-kit is the shared React component package for SmartSell custom modules, tenant extensions, and isolated integrations.
It provides the public UI contract that external consumers should rely on instead of importing private SmartSell app internals. The package is built around a few core goals:
- theme-aware components powered by
smartsell-theme - self-contained styling with no dependency on private SmartSell CSS files
- predictable APIs for forms, modals, data tables, and common surfaces
- async-friendly primitives for remote search and server-driven datasets
Highlights
- React 18-first component API
- Consistent theme integration through
smartsell-theme - Form primitives such as
Input,Textarea,Checkbox,DatePicker,Select, andSelectAsync - Display and layout primitives such as
Card,CardSmall, andSeparator - Modal helpers including
Modal,ComposableModal, andcreateModalSlotIds - Data components including
TableandTableAsync - Brand images published from the dedicated
smartsell-ui-kit/imgsubpath - Local Storybook workspace for visual QA before release
Requirements
- React 18+
- React DOM 18+
smartsell-theme
Installation
npm install react@^18 react-dom@^18 smartsell-theme smartsell-ui-kitQuick start
Wrap your module with ThemeProvider from smartsell-theme, then consume the UI kit components normally.
import { ThemeProvider } from 'smartsell-theme';
import { Button, Card } from 'smartsell-ui-kit';
export default function ExamplePage() {
return (
<ThemeProvider>
<Card title="Example" eyebrow="UI kit">
<Button onClick={() => window.history.back()}>
Go back
</Button>
</Card>
</ThemeProvider>
);
}Main exports
Button,Checkbox,DatePicker,Input,Textarea,Select,SelectAsyncCard,CardSmall,SeparatorModal,ComposableModal,createModalSlotIdsTable,TableAsyncuseUiKitThemesmartsell-ui-kit/imgfor branded logo, icon, favicon, and background image components
Images subpath
Import brand images from the dedicated img subpath so the main entrypoint stays focused on UI primitives.
import { Icon192, Logo, Texture1 } from 'smartsell-ui-kit/img';
export default function BrandHeader() {
return (
<div style={{ display: 'grid', gap: 16 }}>
<Logo style={{ width: 240 }} />
<Icon192 size={72} alt="SmartSell app icon" />
<Texture1 decorative style={{ width: '100%', maxWidth: 420 }} />
</div>
);
}All image components resolve their source from the active smartsell-theme context. Right now light and dark modes point to the same files, but the submodule already swaps sources by theme variant so future dark-specific images only require changing the mapped filenames.
Composable modal pattern
Use ComposableModal when the page should own modal state and business logic, while host applications or extension layers customize the header, body, and footer through stable slot ids.
import { useState } from 'react';
import { Button, ComposableModal, createModalSlotIds } from 'smartsell-ui-kit';
const slotIds = createModalSlotIds('order-review');
export default function ExamplePage() {
const [isOpen, setIsOpen] = useState(false);
const submitOrder = () => {
setIsOpen(false);
};
return (
<>
<Button onClick={() => setIsOpen(true)}>Open order review</Button>
<ComposableModal
isOpen={isOpen}
onClose={() => setIsOpen(false)}
onConfirm={submitOrder}
title="Order review"
slotBaseId="order-review"
renderSlot={({ id, context, children }) => {
if (id === slotIds.footer) {
return (
<Button onClick={() => context.confirm?.()}>
Send order {context.payload?.orderNumber}
</Button>
);
}
return children;
}}
payload={{ orderNumber: 'SO-1001', total: 421.9 }}
>
<div>Default modal body</div>
</ComposableModal>
</>
);
}Slot ids generated by createModalSlotIds('order-review'):
order-review:modal-headerorder-review:modal-bodyorder-review:modal-footer
This pattern is useful when:
- the page owns data loading, permissions, and submission flow
- the host app needs stable extension points instead of private imports
- multiple modal variants should reuse the same contract
Async components
SelectAsync is designed for remote option sources while preserving the same interaction model as the regular Select.
import { useState } from 'react';
import { SelectAsync, type SelectAsyncOption } from 'smartsell-ui-kit';
type CustomerOption = SelectAsyncOption<string>;
function CustomerField() {
const [value, setValue] = useState<CustomerOption | null>(null);
return (
<SelectAsync
label="Customer"
value={value}
onChange={setValue}
loadOptions={async (query, loadedOptions, { page, signal }) => {
const response = await fetch(`/api/customers?search=${query}&page=${page}`, { signal });
const data = await response.json();
return {
options: data.items.map((item: { id: string; tradeName: string }) => ({
value: item.id,
label: item.tradeName,
})),
hasMore: data.hasMore,
};
}}
/>
);
}TableAsync follows the same idea for remote datasets, with debounced search and request cancellation support built into the component flow.
Development workflow
Useful local commands:
npm run storybook
npm run test
npm run validate
npm run validate:releaseRun Storybook in Docker when you want containerized local development:
docker compose up --buildStorybook workspace
The repository includes a local Storybook workspace for component review and design QA.
- grouped navigation for buttons, surfaces, forms, modals, and tables
- a dedicated
Imagessection covering logos, icons, and backgrounds fromsmartsell-ui-kit/img - live Controls for props and responsive behavior
- a local
ThemeProviderpreview using the samesmartsell-themecontract expected by host apps - a
Theme/Colorsplayground for temporary Storybook-only token overrides
Storybook is development-only. It is not published to npm because the package only ships dist, README.md, and LICENSE.
Theme overrides from the Theme/Colors story affect only the local Storybook session. Runtime consumers still read theme values from their host application's smartsell-theme integration.
Release checklist
Before publishing a new version:
npm run validate
npm run build:storybook
npm pack --dry-run
npm publishLicense
This package is distributed under the SmartSell proprietary license described in LICENSE.
