@globalpayments-private/gpds-pattern-web-react 0.0.0-stage → 0.1.1-pr.52.gae8887e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components.d.ts +12 -0
- package/dist/components.js +13 -0
- package/dist/composition/admin.d.ts +60 -0
- package/dist/composition/admin.js +29 -0
- package/dist/composition/children.d.ts +3 -0
- package/dist/composition/children.js +7 -0
- package/dist/composition/custom-routes.d.ts +88 -0
- package/dist/composition/custom-routes.js +76 -0
- package/dist/composition/index.d.ts +55 -0
- package/dist/composition/index.js +48 -0
- package/dist/composition/list.d.ts +220 -0
- package/dist/composition/list.js +90 -0
- package/dist/composition/record.d.ts +137 -0
- package/dist/composition/record.js +257 -0
- package/dist/composition/reference-many.d.ts +86 -0
- package/dist/composition/reference-many.js +60 -0
- package/dist/composition/reference.d.ts +13 -0
- package/dist/composition/reference.js +17 -0
- package/dist/composition/resource.d.ts +36 -0
- package/dist/composition/resource.js +52 -0
- package/dist/composition/simple-form.d.ts +96 -0
- package/dist/composition/simple-form.js +82 -0
- package/dist/gp-admin.d.ts +9 -0
- package/dist/gp-admin.js +20 -0
- package/dist/gp-pattern-list.d.ts +11 -0
- package/dist/gp-pattern-list.js +22 -0
- package/dist/gp-pattern-record.d.ts +12 -0
- package/dist/gp-pattern-record.js +23 -0
- package/dist/gp-pattern-reference-input.d.ts +8 -0
- package/dist/gp-pattern-reference-input.js +19 -0
- package/dist/gp-pattern-wizard.d.ts +11 -0
- package/dist/gp-pattern-wizard.js +22 -0
- package/dist/gp-resource.d.ts +5 -0
- package/dist/gp-resource.js +17 -0
- package/dist/uds-pattern-login-card.d.ts +13 -0
- package/dist/uds-pattern-login-card.js +22 -0
- package/dist/uds-pattern-login-page.d.ts +5 -0
- package/dist/uds-pattern-login-page.js +17 -0
- package/package.json +54 -4
- package/README.md +0 -3
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file was automatically generated by the Stencil React Output Target.
|
|
3
|
+
* Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
|
|
4
|
+
*/
|
|
5
|
+
export { GpAdmin } from './gp-admin.js';
|
|
6
|
+
export { GpPatternList } from './gp-pattern-list.js';
|
|
7
|
+
export { GpPatternRecord } from './gp-pattern-record.js';
|
|
8
|
+
export { GpPatternReferenceInput } from './gp-pattern-reference-input.js';
|
|
9
|
+
export { GpPatternWizard } from './gp-pattern-wizard.js';
|
|
10
|
+
export { GpResource } from './gp-resource.js';
|
|
11
|
+
export { UdsPatternLoginCard } from './uds-pattern-login-card.js';
|
|
12
|
+
export { UdsPatternLoginPage } from './uds-pattern-login-page.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file was automatically generated by the Stencil React Output Target.
|
|
3
|
+
* Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
|
|
4
|
+
*/
|
|
5
|
+
/* eslint-disable */
|
|
6
|
+
export { GpAdmin } from './gp-admin.js';
|
|
7
|
+
export { GpPatternList } from './gp-pattern-list.js';
|
|
8
|
+
export { GpPatternRecord } from './gp-pattern-record.js';
|
|
9
|
+
export { GpPatternReferenceInput } from './gp-pattern-reference-input.js';
|
|
10
|
+
export { GpPatternWizard } from './gp-pattern-wizard.js';
|
|
11
|
+
export { GpResource } from './gp-resource.js';
|
|
12
|
+
export { UdsPatternLoginCard } from './uds-pattern-login-card.js';
|
|
13
|
+
export { UdsPatternLoginPage } from './uds-pattern-login-page.js';
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type ReactElement, type ReactNode } from 'react';
|
|
2
|
+
import type { UdsAdminLabels, UdsAuthProvider, UdsDataProvider, UdsNotificationProvider, UdsQueryProvider, UdsRouterProvider } from '@globalpayments-private/gpds-pattern-web';
|
|
3
|
+
import { type UdsPageBuilder } from './custom-routes';
|
|
4
|
+
/**
|
|
5
|
+
* `Admin`: the root of the composition layer, a thin rename of what
|
|
6
|
+
* `gp-admin` already was (ADR 0012 already treats `<gp-admin><gp-resource/>
|
|
7
|
+
* </gp-admin>` as a composition, not as config). `Admin` exists only because
|
|
8
|
+
* real react-admin's root is called `<Admin>`
|
|
9
|
+
* (https://marmelab.com/react-admin/Tutorial.html) — see
|
|
10
|
+
* `docs/specs/north-star-composition.md` §9.
|
|
11
|
+
*
|
|
12
|
+
* `children` are forwarded untouched: a `<Resource>` passed here renders as a
|
|
13
|
+
* real `<gp-resource>` child element, which is how `gp-admin` discovers its
|
|
14
|
+
* resources today (`admin.config.resources` is the alternative path, not a
|
|
15
|
+
* replacement — see `UdsAdminConfig.resources`'s own doc).
|
|
16
|
+
*
|
|
17
|
+
* `<CustomRoutes>` is the one child `Admin` also *reads*, because a custom
|
|
18
|
+
* page is half configuration (`config.pages`) and half slotted content
|
|
19
|
+
* (`<section slot="page-…">`). Both halves are produced here so the consumer
|
|
20
|
+
* only ever writes react-admin's tree — see `custom-routes.tsx` for why that
|
|
21
|
+
* is the one marker pair that renders.
|
|
22
|
+
*
|
|
23
|
+
* Written with `createElement` rather than JSX: this package's shared Jest
|
|
24
|
+
* config compiles every `.tsx` with Stencil's own `h`-pragma transform (the
|
|
25
|
+
* rest of `src/` needs exactly that), so JSX syntax here would silently
|
|
26
|
+
* target the wrong runtime under test. `createElement` is what JSX compiles
|
|
27
|
+
* to either way.
|
|
28
|
+
*/
|
|
29
|
+
export interface AdminProps {
|
|
30
|
+
data?: UdsDataProvider;
|
|
31
|
+
auth?: UdsAuthProvider;
|
|
32
|
+
/**
|
|
33
|
+
* The query cache, mirroring react-admin's `queryClient`
|
|
34
|
+
* (https://marmelab.com/react-admin/Admin.html#queryclient).
|
|
35
|
+
*/
|
|
36
|
+
query?: UdsQueryProvider;
|
|
37
|
+
/**
|
|
38
|
+
* The router, mirroring react-admin's `basename`/router configuration. A
|
|
39
|
+
* demo served from GitHub Pages needs `createHistoryRouter({ hash: true })`
|
|
40
|
+
* here, so this is not an advanced seam — it is the first thing a deployed
|
|
41
|
+
* app sets.
|
|
42
|
+
*/
|
|
43
|
+
router?: UdsRouterProvider;
|
|
44
|
+
/** Notifications, mirroring react-admin's `notification` slot. */
|
|
45
|
+
notifications?: UdsNotificationProvider;
|
|
46
|
+
/** Copy overrides, the narrow part of react-admin's `i18nProvider`. */
|
|
47
|
+
labels?: Partial<UdsAdminLabels>;
|
|
48
|
+
/**
|
|
49
|
+
* The landing screen, mirroring react-admin's `dashboard`
|
|
50
|
+
* (https://marmelab.com/react-admin/Admin.html#dashboard).
|
|
51
|
+
*
|
|
52
|
+
* Takes a React node or a `() => Node` builder, for the same reason
|
|
53
|
+
* `<Route element>` does: this library's framework-neutral pages return
|
|
54
|
+
* plain DOM and are mirrored unchanged into the vanilla, Vue and Angular
|
|
55
|
+
* variants of the same demo (ADR 0017, ADR 0030).
|
|
56
|
+
*/
|
|
57
|
+
dashboard?: ReactNode | UdsPageBuilder;
|
|
58
|
+
children?: ReactElement | ReactElement[];
|
|
59
|
+
}
|
|
60
|
+
export declare function Admin({ data, auth, query, router, notifications, labels, dashboard, children, }: AdminProps): ReactElement;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createElement } from 'react';
|
|
2
|
+
// Relative, not the `./react` package subpath: that subpath has no `require`
|
|
3
|
+
// condition (ESM-only, matching the rest of the generated bindings), which
|
|
4
|
+
// would break loading this file under Jest's CommonJS test runner.
|
|
5
|
+
import { GpAdmin } from '@globalpayments-private/gpds-pattern-web-react';
|
|
6
|
+
import { mountBuilder, pageSectionsFrom, pagesFrom, routesFromChildren, } from './custom-routes';
|
|
7
|
+
export function Admin({ data, auth, query, router, notifications, labels, dashboard, children, }) {
|
|
8
|
+
const routes = routesFromChildren(children);
|
|
9
|
+
const config = {
|
|
10
|
+
data,
|
|
11
|
+
auth,
|
|
12
|
+
query,
|
|
13
|
+
router,
|
|
14
|
+
notifications,
|
|
15
|
+
labels,
|
|
16
|
+
pages: pagesFrom(routes),
|
|
17
|
+
};
|
|
18
|
+
return createElement(GpAdmin, { config, hasDashboard: dashboard !== undefined }, children, ...pageSectionsFrom(routes), ...dashboardSection(dashboard));
|
|
19
|
+
}
|
|
20
|
+
/** The `<div slot="dashboard">` the shell renders its landing screen into, when there is one. */
|
|
21
|
+
function dashboardSection(dashboard) {
|
|
22
|
+
if (dashboard === undefined) {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
const isBuilder = typeof dashboard === 'function';
|
|
26
|
+
return [
|
|
27
|
+
createElement('div', { key: 'dashboard', slot: 'dashboard', ...(isBuilder ? { ref: mountBuilder(dashboard) } : {}) }, isBuilder ? undefined : dashboard),
|
|
28
|
+
];
|
|
29
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** `React.Children.toArray`, without taking a hard dependency on the `React` default export. */
|
|
2
|
+
export function reactChildrenToArray(children) {
|
|
3
|
+
if (children === undefined || children === null || children === false) {
|
|
4
|
+
return [];
|
|
5
|
+
}
|
|
6
|
+
return Array.isArray(children) ? children.flat(Infinity) : [children];
|
|
7
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { type ReactElement, type ReactNode } from 'react';
|
|
2
|
+
import type { UdsAdminPageDefinition } from '@globalpayments-private/gpds-pattern-web';
|
|
3
|
+
/**
|
|
4
|
+
* `CustomRoutes` and `Route`: the composition spelling of
|
|
5
|
+
* `UdsAdminConfig.pages`, mirroring real react-admin's
|
|
6
|
+
* https://marmelab.com/react-admin/CustomRoutes.html:
|
|
7
|
+
*
|
|
8
|
+
* ```tsx
|
|
9
|
+
* <Admin data={data}>
|
|
10
|
+
* <Resource name="merchants">…</Resource>
|
|
11
|
+
* <CustomRoutes>
|
|
12
|
+
* <Route path="/reports/settlement" name="settlement" label="Settlement" element={buildSettlementReport} />
|
|
13
|
+
* </CustomRoutes>
|
|
14
|
+
* </Admin>
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* ## Why these are the one pair of markers that really render
|
|
18
|
+
*
|
|
19
|
+
* Every other marker in this layer is read and discarded, because what it
|
|
20
|
+
* describes is *configuration* that ends up on a `config` prop. A custom page
|
|
21
|
+
* is not only configuration: `gp-admin` matches `pages[].path` and then
|
|
22
|
+
* renders whatever is in the matching **named slot** — `{ name: 'settlement' }`
|
|
23
|
+
* pairs with `<section slot="page-settlement">`. The definition and the
|
|
24
|
+
* content are two halves of one thing, and only one half is a plain object.
|
|
25
|
+
*
|
|
26
|
+
* So `Admin` does both: it reads `<CustomRoutes>` into `config.pages`, and it
|
|
27
|
+
* renders each `<Route>`'s `element` into the `<section slot="page-…">` the
|
|
28
|
+
* shell will look for. The consumer writes react-admin's tree and never sees
|
|
29
|
+
* the slot convention, which is the point.
|
|
30
|
+
*
|
|
31
|
+
* ## Why `element` accepts a DOM builder as well as a React node
|
|
32
|
+
*
|
|
33
|
+
* React-admin's `element` is necessarily a React element — react-admin is a
|
|
34
|
+
* React framework. This library is not: its demos' custom pages are
|
|
35
|
+
* framework-neutral builders that return plain DOM and reach the shell
|
|
36
|
+
* through `requestContext` (ADR 0017), and they are mirrored unchanged into
|
|
37
|
+
* the vanilla, Vue and Angular variants of the same demo. Accepting only a
|
|
38
|
+
* React node would mean every custom page had to be rewritten as a React tree
|
|
39
|
+
* purely to satisfy the shape of a tag — which would end the single mirrored
|
|
40
|
+
* source those demos are built on, to buy nothing.
|
|
41
|
+
*
|
|
42
|
+
* So `element` takes either: a React node, rendered as the slot's children;
|
|
43
|
+
* or a `() => Node` builder, mounted into the slot through a ref on first
|
|
44
|
+
* attach. See ADR 0030.
|
|
45
|
+
*/
|
|
46
|
+
/** A builder that returns a plain DOM node — what this library's framework-neutral pages are. */
|
|
47
|
+
export type UdsPageBuilder = () => Node;
|
|
48
|
+
export interface RouteProps extends Omit<UdsAdminPageDefinition, 'name'> {
|
|
49
|
+
/**
|
|
50
|
+
* The page's stable key, used for the slot name (`page-<name>`).
|
|
51
|
+
*
|
|
52
|
+
* Defaults to a slug of `path`, so the common case needs only `path` and
|
|
53
|
+
* `element`, as in react-admin.
|
|
54
|
+
*/
|
|
55
|
+
name?: string;
|
|
56
|
+
/** The page's content: a React node, or a builder returning a DOM node. */
|
|
57
|
+
element: ReactNode | UdsPageBuilder;
|
|
58
|
+
}
|
|
59
|
+
/** Marker component for one custom page. Never rendered directly — see the module doc. */
|
|
60
|
+
export declare function Route(_props: RouteProps): null;
|
|
61
|
+
export interface CustomRoutesProps {
|
|
62
|
+
/** `<Route>` children. */
|
|
63
|
+
children?: ReactNode;
|
|
64
|
+
}
|
|
65
|
+
/** Marker component for the custom-page set. Never rendered directly — see the module doc. */
|
|
66
|
+
export declare function CustomRoutes(_props: CustomRoutesProps): null;
|
|
67
|
+
/** One `<Route>`, resolved into the definition and the content the shell needs separately. */
|
|
68
|
+
interface ResolvedRoute {
|
|
69
|
+
definition: UdsAdminPageDefinition;
|
|
70
|
+
element: ReactNode | UdsPageBuilder;
|
|
71
|
+
}
|
|
72
|
+
/** Reads every `<CustomRoutes>` child of an `<Admin>` into its `<Route>`s, in order. */
|
|
73
|
+
export declare function routesFromChildren(children: ReactNode): ResolvedRoute[];
|
|
74
|
+
/** The `pages` member a resolved route set stands for, or `undefined` when there are none. */
|
|
75
|
+
export declare function pagesFrom(routes: ResolvedRoute[]): UdsAdminPageDefinition[] | undefined;
|
|
76
|
+
/**
|
|
77
|
+
* The `<section slot="page-…">` elements `gp-admin` renders custom pages into.
|
|
78
|
+
*
|
|
79
|
+
* A React-node `element` becomes the section's children. A builder is mounted
|
|
80
|
+
* through a ref: the section is created empty and the builder's node appended
|
|
81
|
+
* once, on attach. The ref deliberately rebuilds nothing on re-render — these
|
|
82
|
+
* pages own their own DOM and their own updates (ADR 0017), and re-running a
|
|
83
|
+
* builder under them would throw away the state they are holding.
|
|
84
|
+
*/
|
|
85
|
+
export declare function pageSectionsFrom(routes: ResolvedRoute[]): ReactElement[];
|
|
86
|
+
/** A ref callback that appends the builder's node once, the first time the host attaches. */
|
|
87
|
+
export declare function mountBuilder(build: UdsPageBuilder): (host: Element | null) => void;
|
|
88
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { createElement } from 'react';
|
|
2
|
+
import { reactChildrenToArray } from './children';
|
|
3
|
+
/** Marker component for one custom page. Never rendered directly — see the module doc. */
|
|
4
|
+
export function Route(_props) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
/** Marker component for the custom-page set. Never rendered directly — see the module doc. */
|
|
8
|
+
export function CustomRoutes(_props) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
/** Reads every `<CustomRoutes>` child of an `<Admin>` into its `<Route>`s, in order. */
|
|
12
|
+
export function routesFromChildren(children) {
|
|
13
|
+
const resolved = [];
|
|
14
|
+
reactChildrenToArray(children).forEach((child) => {
|
|
15
|
+
if (!isElementOfType(child, CustomRoutes)) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
reactChildrenToArray(child.props.children).forEach((route) => {
|
|
19
|
+
if (!isElementOfType(route, Route)) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const { name, element, ...page } = route.props;
|
|
23
|
+
resolved.push({ definition: { ...page, name: name ?? slug(page.path) }, element });
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
return resolved;
|
|
27
|
+
}
|
|
28
|
+
/** The `pages` member a resolved route set stands for, or `undefined` when there are none. */
|
|
29
|
+
export function pagesFrom(routes) {
|
|
30
|
+
return routes.length ? routes.map((route) => route.definition) : undefined;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The `<section slot="page-…">` elements `gp-admin` renders custom pages into.
|
|
34
|
+
*
|
|
35
|
+
* A React-node `element` becomes the section's children. A builder is mounted
|
|
36
|
+
* through a ref: the section is created empty and the builder's node appended
|
|
37
|
+
* once, on attach. The ref deliberately rebuilds nothing on re-render — these
|
|
38
|
+
* pages own their own DOM and their own updates (ADR 0017), and re-running a
|
|
39
|
+
* builder under them would throw away the state they are holding.
|
|
40
|
+
*/
|
|
41
|
+
export function pageSectionsFrom(routes) {
|
|
42
|
+
return routes.map(({ definition, element }) => createElement('section', {
|
|
43
|
+
key: definition.name,
|
|
44
|
+
slot: `page-${definition.name}`,
|
|
45
|
+
...(isBuilder(element) ? { ref: mountBuilder(element) } : {}),
|
|
46
|
+
}, isBuilder(element) ? undefined : element));
|
|
47
|
+
}
|
|
48
|
+
/** A ref callback that appends the builder's node once, the first time the host attaches. */
|
|
49
|
+
export function mountBuilder(build) {
|
|
50
|
+
return (host) => {
|
|
51
|
+
if (host && !host.firstChild) {
|
|
52
|
+
host.appendChild(build());
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* A function `element` is a builder, not a React node.
|
|
58
|
+
*
|
|
59
|
+
* React elements are objects and strings are strings, so a function is never
|
|
60
|
+
* a valid `ReactNode` — there is no ambiguity to resolve here, which is why
|
|
61
|
+
* the two forms can share one prop without a discriminator.
|
|
62
|
+
*/
|
|
63
|
+
function isBuilder(element) {
|
|
64
|
+
return typeof element === 'function';
|
|
65
|
+
}
|
|
66
|
+
/** `'/reports/settlement'` → `'reports-settlement'`. */
|
|
67
|
+
function slug(path) {
|
|
68
|
+
return path
|
|
69
|
+
.toLowerCase()
|
|
70
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
71
|
+
.replace(/^-+|-+$/g, '');
|
|
72
|
+
}
|
|
73
|
+
/** A React element whose `type` is exactly this marker function. */
|
|
74
|
+
function isElementOfType(node, type) {
|
|
75
|
+
return typeof node === 'object' && node !== null && 'type' in node && node.type === type;
|
|
76
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The composition layer: a thin, additive JSX authoring surface over the
|
|
3
|
+
* existing config-as-data public API (ADR 0028). None of this is a new
|
|
4
|
+
* runtime — `Admin`/`Resource` are renamed pass-throughs to `gp-admin`/
|
|
5
|
+
* `gp-resource` (ADR 0012), and the rest are read once into the same
|
|
6
|
+
* `UdsAdminResource` shape a hand-written `config` object already produces.
|
|
7
|
+
*
|
|
8
|
+
* ## What mirrors react-admin, and what does not
|
|
9
|
+
*
|
|
10
|
+
* **Mirrors react-admin one-to-one** (https://marmelab.com/react-admin/Tutorial.html):
|
|
11
|
+
* `Admin`, `Resource`, `List`, `DataTable`, `DataTable.Col`, `Show`, `Edit`,
|
|
12
|
+
* `Create`, `SimpleForm` and its inputs, `TabbedShowLayout` /
|
|
13
|
+
* `TabbedShowLayout.Tab`, `ReferenceManyField`, `CustomRoutes` / `Route`.
|
|
14
|
+
*
|
|
15
|
+
* **Deliberate extensions beyond react-admin** (ADR 0030): `Board` and
|
|
16
|
+
* `CardGrid`. React-admin ships no kanban-board and no card-grid list view
|
|
17
|
+
* anywhere in its core — Marmelab's own Kanban is built from scratch in a
|
|
18
|
+
* tutorial and their Atomic CRM demo hand-rolls its deal pipeline. These
|
|
19
|
+
* exist because this library's pattern layer already ships both as
|
|
20
|
+
* `kind: 'board' | 'grid'` layouts (ADR 0016). They follow react-admin's
|
|
21
|
+
* grammar even so: they are *view children of `<List>`*, siblings of
|
|
22
|
+
* `<DataTable>`, never replacements for `<List>` itself.
|
|
23
|
+
*
|
|
24
|
+
* **Deliberately absent**: `Filter` — react-admin has no such tag, only a
|
|
25
|
+
* `filters` array *prop*, which `List.filters` mirrors directly. And no
|
|
26
|
+
* `Money`/`Status` tags, which react-admin likewise does not ship.
|
|
27
|
+
*
|
|
28
|
+
* **The two places this surface used to be narrower than react-admin are
|
|
29
|
+
* closed** (ADR 0031). `Show`/`Edit`/`Create` may carry genuinely different
|
|
30
|
+
* field sets — they are merged into the shell's one `fields` array and gated
|
|
31
|
+
* per mode via `UdsRecordField.modes` — and `ReferenceManyField` works both
|
|
32
|
+
* inside a view, reading the record's id from context as react-admin does,
|
|
33
|
+
* and as a child of `Resource` with an explicit `id`, which keeps working.
|
|
34
|
+
*
|
|
35
|
+
* What remains loud is only what one shell rendering one tab strip genuinely
|
|
36
|
+
* cannot express: wrappers that use different layouts, order the same fields
|
|
37
|
+
* differently, or name, order or label tabs differently. Those are rejected
|
|
38
|
+
* at read time with an error naming the limitation, never approximated — see
|
|
39
|
+
* `record.tsx`, `reference-many.tsx`, ADR 0030, ADR 0031 and
|
|
40
|
+
* `docs/specs/north-star-composition.md` §12 and §14.
|
|
41
|
+
*/
|
|
42
|
+
export { Admin } from './admin';
|
|
43
|
+
export type { AdminProps } from './admin';
|
|
44
|
+
export { Resource } from './resource';
|
|
45
|
+
export type { ResourceProps } from './resource';
|
|
46
|
+
export { List, DataTable, Board, CardGrid } from './list';
|
|
47
|
+
export type { ListProps, ListViewActionProps, DataTableProps, DataTableColProps, BoardProps, CardGridProps, } from './list';
|
|
48
|
+
export { Show, Edit, Create, TabbedShowLayout } from './record';
|
|
49
|
+
export type { ShowProps, EditProps, CreateProps, TabbedShowLayoutProps, TabbedShowLayoutTabProps, } from './record';
|
|
50
|
+
export { ReferenceManyField } from './reference-many';
|
|
51
|
+
export type { ReferenceManyFieldProps } from './reference-many';
|
|
52
|
+
export { CustomRoutes, Route } from './custom-routes';
|
|
53
|
+
export type { CustomRoutesProps, RouteProps, UdsPageBuilder } from './custom-routes';
|
|
54
|
+
export { SimpleForm, TextInput, NumberInput, BooleanInput, DateInput, SelectInput, ReferenceInput, } from './simple-form';
|
|
55
|
+
export type { SimpleFormProps, TextInputProps, NumberInputProps, BooleanInputProps, DateInputProps, SelectInputProps, ReferenceInputProps, } from './simple-form';
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The composition layer: a thin, additive JSX authoring surface over the
|
|
3
|
+
* existing config-as-data public API (ADR 0028). None of this is a new
|
|
4
|
+
* runtime — `Admin`/`Resource` are renamed pass-throughs to `gp-admin`/
|
|
5
|
+
* `gp-resource` (ADR 0012), and the rest are read once into the same
|
|
6
|
+
* `UdsAdminResource` shape a hand-written `config` object already produces.
|
|
7
|
+
*
|
|
8
|
+
* ## What mirrors react-admin, and what does not
|
|
9
|
+
*
|
|
10
|
+
* **Mirrors react-admin one-to-one** (https://marmelab.com/react-admin/Tutorial.html):
|
|
11
|
+
* `Admin`, `Resource`, `List`, `DataTable`, `DataTable.Col`, `Show`, `Edit`,
|
|
12
|
+
* `Create`, `SimpleForm` and its inputs, `TabbedShowLayout` /
|
|
13
|
+
* `TabbedShowLayout.Tab`, `ReferenceManyField`, `CustomRoutes` / `Route`.
|
|
14
|
+
*
|
|
15
|
+
* **Deliberate extensions beyond react-admin** (ADR 0030): `Board` and
|
|
16
|
+
* `CardGrid`. React-admin ships no kanban-board and no card-grid list view
|
|
17
|
+
* anywhere in its core — Marmelab's own Kanban is built from scratch in a
|
|
18
|
+
* tutorial and their Atomic CRM demo hand-rolls its deal pipeline. These
|
|
19
|
+
* exist because this library's pattern layer already ships both as
|
|
20
|
+
* `kind: 'board' | 'grid'` layouts (ADR 0016). They follow react-admin's
|
|
21
|
+
* grammar even so: they are *view children of `<List>`*, siblings of
|
|
22
|
+
* `<DataTable>`, never replacements for `<List>` itself.
|
|
23
|
+
*
|
|
24
|
+
* **Deliberately absent**: `Filter` — react-admin has no such tag, only a
|
|
25
|
+
* `filters` array *prop*, which `List.filters` mirrors directly. And no
|
|
26
|
+
* `Money`/`Status` tags, which react-admin likewise does not ship.
|
|
27
|
+
*
|
|
28
|
+
* **The two places this surface used to be narrower than react-admin are
|
|
29
|
+
* closed** (ADR 0031). `Show`/`Edit`/`Create` may carry genuinely different
|
|
30
|
+
* field sets — they are merged into the shell's one `fields` array and gated
|
|
31
|
+
* per mode via `UdsRecordField.modes` — and `ReferenceManyField` works both
|
|
32
|
+
* inside a view, reading the record's id from context as react-admin does,
|
|
33
|
+
* and as a child of `Resource` with an explicit `id`, which keeps working.
|
|
34
|
+
*
|
|
35
|
+
* What remains loud is only what one shell rendering one tab strip genuinely
|
|
36
|
+
* cannot express: wrappers that use different layouts, order the same fields
|
|
37
|
+
* differently, or name, order or label tabs differently. Those are rejected
|
|
38
|
+
* at read time with an error naming the limitation, never approximated — see
|
|
39
|
+
* `record.tsx`, `reference-many.tsx`, ADR 0030, ADR 0031 and
|
|
40
|
+
* `docs/specs/north-star-composition.md` §12 and §14.
|
|
41
|
+
*/
|
|
42
|
+
export { Admin } from './admin';
|
|
43
|
+
export { Resource } from './resource';
|
|
44
|
+
export { List, DataTable, Board, CardGrid } from './list';
|
|
45
|
+
export { Show, Edit, Create, TabbedShowLayout } from './record';
|
|
46
|
+
export { ReferenceManyField } from './reference-many';
|
|
47
|
+
export { CustomRoutes, Route } from './custom-routes';
|
|
48
|
+
export { SimpleForm, TextInput, NumberInput, BooleanInput, DateInput, SelectInput, ReferenceInput, } from './simple-form';
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { UdsAdminListConfig, UdsListAction, UdsListColumn, UdsListFilterField, UdsListLane, UdsListMedia } from '@globalpayments-private/gpds-pattern-web';
|
|
3
|
+
/**
|
|
4
|
+
* `List`, `DataTable` and `DataTable.Col`: non-rendering marker components
|
|
5
|
+
* read by `Resource` (`resource.tsx`) to build a `UdsAdminListConfig` before
|
|
6
|
+
* anything reaches `gp-resource`/`gp-pattern-list`.
|
|
7
|
+
*
|
|
8
|
+
* ## Why these never render
|
|
9
|
+
*
|
|
10
|
+
* Every `@slot` this library ships holds markup rendered verbatim; none holds
|
|
11
|
+
* a schema read back out and discarded. `List`/`DataTable`/`DataTable.Col`
|
|
12
|
+
* are the one necessary exception ADR 0009 already accepts for
|
|
13
|
+
* `<Column>`/`<Filter>`-shaped authoring, so they stay here rather than
|
|
14
|
+
* becoming real elements: each one is called with its props and children at
|
|
15
|
+
* read time (`listConfigFromChildren`, below), and the resulting
|
|
16
|
+
* `UdsAdminListConfig` is handed to `<GpResource config>` — the same public
|
|
17
|
+
* prop a hand-written consumer uses (ADR 0012's equivalence rule). None of
|
|
18
|
+
* these functions is ever invoked by React; they only need a stable function
|
|
19
|
+
* identity so the reader below can recognise which element is which.
|
|
20
|
+
*
|
|
21
|
+
* ## Why the set mirrors what react-admin ships — and where it deliberately does not
|
|
22
|
+
*
|
|
23
|
+
* Per explicit direction, this composition layer duplicates real
|
|
24
|
+
* react-admin's shipped API (https://marmelab.com/react-admin/Tutorial.html)
|
|
25
|
+
* one-to-one rather than approximating it: `DataTable` is named after
|
|
26
|
+
* react-admin's actual table view component, and `DataTable.Col` after
|
|
27
|
+
* `DataTable.Col` — see `docs/specs/north-star-composition.md` §10 for the
|
|
28
|
+
* full reasoning. `Filter` is dropped entirely: react-admin has no such tag,
|
|
29
|
+
* only a `filters` array *prop* of input elements, which `List.filters`
|
|
30
|
+
* mirrors directly (`UdsListFilterField` is already the plain-object shape
|
|
31
|
+
* that prop wants, so there is nothing to translate).
|
|
32
|
+
*
|
|
33
|
+
* `Board` and `CardGrid` are the two **deliberate extensions beyond
|
|
34
|
+
* react-admin** (ADR 0030). React-admin ships no kanban-board and no
|
|
35
|
+
* card-grid list view anywhere in its core, under any name or shape:
|
|
36
|
+
* Marmelab's own Kanban is built from scratch in a tutorial
|
|
37
|
+
* (https://marmelab.com/blog/2023/07/28/create-a-kanban-board-in-react-admin.html)
|
|
38
|
+
* and their Atomic CRM demo hand-rolls its deal pipeline. They exist here
|
|
39
|
+
* because this library's pattern layer already ships both presentations as
|
|
40
|
+
* `kind: 'board' | 'grid'` layouts (ADR 0016) and the authoring surface was
|
|
41
|
+
* the only thing that could not reach them.
|
|
42
|
+
*
|
|
43
|
+
* They are shaped to react-admin's *grammar* even though the components are
|
|
44
|
+
* ours: in react-admin **the `<List>` element never swaps — only its view
|
|
45
|
+
* child does** (`<DataTable>`, `<SimpleList>`, `<SingleFieldList>`). So these
|
|
46
|
+
* are siblings of `DataTable` *inside* `<List>`, never replacements for
|
|
47
|
+
* `<List>` itself. A `<Board>` that replaced `<List>` would discard the
|
|
48
|
+
* querying, URL state, sorting, filtering, pagination and capability gating
|
|
49
|
+
* that `<List>` is, which is the whole claim of ADR 0016.
|
|
50
|
+
*
|
|
51
|
+
* All three view children read the same `<DataTable.Col>` children, because
|
|
52
|
+
* columns are the configuration for *every* layout — moving a list between
|
|
53
|
+
* presentations is one tag and never a rewrite of its columns.
|
|
54
|
+
*
|
|
55
|
+
* ## Where the actions live, and why the two differ
|
|
56
|
+
*
|
|
57
|
+
* `bulkActions` sits on the **view child, not on `<List>`**, because that is
|
|
58
|
+
* where real react-admin puts it: `bulkActionButtons` is a documented prop of
|
|
59
|
+
* `<DataTable>` (https://marmelab.com/react-admin/DataTable.html#props), not
|
|
60
|
+
* of `<List>`. Row *selection* is a property of the thing that renders rows,
|
|
61
|
+
* so the toolbar acting on a selection belongs beside it. Our config object
|
|
62
|
+
* happens to keep `bulkActions` under `list`, and that was not allowed to
|
|
63
|
+
* decide the JSX: the projection translates, exactly as `perPage` is a
|
|
64
|
+
* `<List>` prop that lands outside `list` (see `listConfigFromChildren`).
|
|
65
|
+
*
|
|
66
|
+
* `rowActions` sits there too, and that one **is a deviation**, stated rather
|
|
67
|
+
* than hidden. React-admin has no `rowActions` prop; it spells a row action
|
|
68
|
+
* as an ordinary button inside a column — `<DataTable.Col><EditButton /></…>`.
|
|
69
|
+
* That shape cannot be mirrored here for two independent reasons:
|
|
70
|
+
*
|
|
71
|
+
* 1. **Ours are effects, not elements.** `UdsListAction` is a description of
|
|
72
|
+
* a write (`{ kind: 'update', values }`), executed by the pattern through
|
|
73
|
+
* the staged, capability-gated, undoable path of ADR 0010 — and *not
|
|
74
|
+
* rendered at all* when the adapter cannot service it (ADR 0002 §2). A
|
|
75
|
+
* React element child is an instruction to render something regardless;
|
|
76
|
+
* accepting one would mean either ignoring what it renders or losing the
|
|
77
|
+
* gating, and both are worse than a different spelling.
|
|
78
|
+
* 2. **Two of the three layouts have no columns to put it in.** `board` and
|
|
79
|
+
* `grid` render row actions in the card footer. A column-shaped spelling
|
|
80
|
+
* would be correct for `table` and meaningless for the other two, which
|
|
81
|
+
* breaks the one-tag-swap rule the view children exist to keep.
|
|
82
|
+
*
|
|
83
|
+
* So both are plain array props, for the same reason `filters` is one: the
|
|
84
|
+
* underlying member is already the plain-object shape the prop wants, and
|
|
85
|
+
* inventing a `<RowAction>` tag would add a vocabulary without adding a
|
|
86
|
+
* capability. See ADR 0032.
|
|
87
|
+
*/
|
|
88
|
+
/** A pure-data column: everything `DataTable.Col` accepts except the raw React element wrapper. */
|
|
89
|
+
export interface DataTableColProps {
|
|
90
|
+
field: string;
|
|
91
|
+
label: string;
|
|
92
|
+
sortable?: boolean;
|
|
93
|
+
align?: UdsListColumn['align'];
|
|
94
|
+
width?: string;
|
|
95
|
+
/** `{ resource: 'merchants' }` shorthand — defaults `field` to `'name'`. */
|
|
96
|
+
reference?: string | UdsListColumn['reference'];
|
|
97
|
+
/**
|
|
98
|
+
* A column factory, called as `(field, label) => UdsListColumn` — the exact
|
|
99
|
+
* signature `moneyColumn`/`statusColumn` already export. Mirrors
|
|
100
|
+
* `<DataTable.Col source="email"><EmailField source="email" /></DataTable.Col>`:
|
|
101
|
+
* the child does the real work, the wrapper only carries `field`/`label`.
|
|
102
|
+
*/
|
|
103
|
+
children?: (field: string, label: string) => UdsListColumn;
|
|
104
|
+
}
|
|
105
|
+
/** Marker component for one table column. Never rendered — see the module doc. */
|
|
106
|
+
export declare function DataTableCol(_props: DataTableColProps): null;
|
|
107
|
+
/**
|
|
108
|
+
* The two action props every view child accepts.
|
|
109
|
+
*
|
|
110
|
+
* Factored out rather than repeated three times because `rowActions` renders
|
|
111
|
+
* in a trailing column for `table` and in the card footer for `board` and
|
|
112
|
+
* `grid` — one configuration, three presentations, which is the same rule
|
|
113
|
+
* columns already follow. See the module doc for why these sit on the view
|
|
114
|
+
* child and not on `<List>`.
|
|
115
|
+
*/
|
|
116
|
+
export interface ListViewActionProps {
|
|
117
|
+
/**
|
|
118
|
+
* Actions offered on each record, forwarded to `UdsListConfig.rowActions`.
|
|
119
|
+
*
|
|
120
|
+
* **A deviation from react-admin**, deliberately — see the module doc. An
|
|
121
|
+
* action the adapter cannot service is absent rather than disabled, and
|
|
122
|
+
* `available` — an extension with no react-admin counterpart, like `Board`
|
|
123
|
+
* and `CardGrid` (ADR 0030) — hides one for records it does not apply to.
|
|
124
|
+
*/
|
|
125
|
+
rowActions?: UdsListAction[];
|
|
126
|
+
/**
|
|
127
|
+
* Actions offered on the current selection, forwarded to
|
|
128
|
+
* `UdsListConfig.bulkActions`. Mirrors the placement of react-admin's
|
|
129
|
+
* `bulkActionButtons`, which is likewise a prop of the table view and not
|
|
130
|
+
* of `<List>`.
|
|
131
|
+
*/
|
|
132
|
+
bulkActions?: UdsListAction[];
|
|
133
|
+
}
|
|
134
|
+
export interface DataTableProps extends ListViewActionProps {
|
|
135
|
+
children?: ReactNode;
|
|
136
|
+
}
|
|
137
|
+
/** Marker component for the table view. Never rendered. */
|
|
138
|
+
export declare function DataTable(_props: DataTableProps): null;
|
|
139
|
+
export declare namespace DataTable {
|
|
140
|
+
var Col: typeof DataTableCol;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* **An extension beyond react-admin** (see the module doc): cards distributed
|
|
144
|
+
* into lanes by the value of one field — a pipeline rather than a ledger.
|
|
145
|
+
*
|
|
146
|
+
* The stacks are **lanes**, never "columns". `UdsListBoardLayout` spends a
|
|
147
|
+
* long comment on why the word is spent only once: this library ships a
|
|
148
|
+
* *column chooser*, and a user who opens a menu labelled "Columns" on a
|
|
149
|
+
* kanban and is shown field names has been misled by our vocabulary. The JSX
|
|
150
|
+
* honours the same rule — `groupBy` names the lane-forming field, exactly as
|
|
151
|
+
* the underlying layout member does, so the projection is one-to-one and
|
|
152
|
+
* there is no second vocabulary to keep in step.
|
|
153
|
+
*/
|
|
154
|
+
export interface BoardProps extends ListViewActionProps {
|
|
155
|
+
/** The field whose value forms the lanes. Mirrors `UdsListBoardLayout.groupBy`. */
|
|
156
|
+
groupBy: string;
|
|
157
|
+
/** The lanes to render, in order. Omit to derive them from the data. */
|
|
158
|
+
lanes?: UdsListLane[];
|
|
159
|
+
/** The column whose value heads each card, by `field`. Defaults to the first. */
|
|
160
|
+
title?: string;
|
|
161
|
+
/** Which columns fill the card body, by `field`. Defaults to every other one. */
|
|
162
|
+
fields?: string[];
|
|
163
|
+
/** The narrowest a lane may become, as a CSS length. */
|
|
164
|
+
minLaneWidth?: string;
|
|
165
|
+
/** `<DataTable.Col>` children — the same columns a table would read. */
|
|
166
|
+
children?: ReactNode;
|
|
167
|
+
}
|
|
168
|
+
/** Marker component for the board view. Never rendered — see the module doc. */
|
|
169
|
+
export declare function Board(_props: BoardProps): null;
|
|
170
|
+
/**
|
|
171
|
+
* **An extension beyond react-admin** (see the module doc): rows as a
|
|
172
|
+
* responsive grid of cards — a catalogue rather than a ledger.
|
|
173
|
+
*
|
|
174
|
+
* Named `CardGrid` rather than `Grid` because a bare `Grid` reads as a
|
|
175
|
+
* generic layout primitive (it is one, in every component library this sits
|
|
176
|
+
* beside) rather than as a list *view*, and this is only ever the latter.
|
|
177
|
+
*/
|
|
178
|
+
export interface CardGridProps extends ListViewActionProps {
|
|
179
|
+
/** The column whose value heads each card, by `field`. Defaults to the first. */
|
|
180
|
+
title?: string;
|
|
181
|
+
/** An image to lead each card with. Omit for text-only cards. */
|
|
182
|
+
media?: UdsListMedia;
|
|
183
|
+
/** Which columns fill the card body, by `field`. Defaults to every other one. */
|
|
184
|
+
fields?: string[];
|
|
185
|
+
/** The narrowest a card may become before the grid drops a column, as a CSS length. */
|
|
186
|
+
minColumnWidth?: string;
|
|
187
|
+
/** `<DataTable.Col>` children — the same columns a table would read. */
|
|
188
|
+
children?: ReactNode;
|
|
189
|
+
}
|
|
190
|
+
/** Marker component for the card-grid view. Never rendered — see the module doc. */
|
|
191
|
+
export declare function CardGrid(_props: CardGridProps): null;
|
|
192
|
+
export interface ListProps {
|
|
193
|
+
perPage?: number;
|
|
194
|
+
/**
|
|
195
|
+
* Passed straight through to `UdsAdminListConfig.filters` — a plain prop,
|
|
196
|
+
* not a child element, matching react-admin's own `<List filters={[...]}>`
|
|
197
|
+
* (see the module doc).
|
|
198
|
+
*/
|
|
199
|
+
filters?: UdsListFilterField[];
|
|
200
|
+
/** The view child: `<DataTable>`, `<Board>` or `<CardGrid>`. */
|
|
201
|
+
children?: ReactNode;
|
|
202
|
+
}
|
|
203
|
+
/** Marker component for the list workflow. Never rendered — see the module doc. */
|
|
204
|
+
export declare function List(_props: ListProps): null;
|
|
205
|
+
/**
|
|
206
|
+
* Reads a `<List>` element's props and view child into a `UdsAdminListConfig`
|
|
207
|
+
* — the exact shape `GpResource`'s `config.list` takes (ADR 0022).
|
|
208
|
+
*
|
|
209
|
+
* `perPage` is a scalar, forwarded as `UdsAdminResource.perPage` rather than
|
|
210
|
+
* as a member of this config (see `admin-config.ts`'s `perPage` doc) — the
|
|
211
|
+
* caller (`resource.tsx`) is responsible for placing it there; this function
|
|
212
|
+
* only returns what belongs under `list`.
|
|
213
|
+
*
|
|
214
|
+
* The view child decides the `layout` and nothing else: all three read the
|
|
215
|
+
* same `<DataTable.Col>` children into the same `columns`, which is what
|
|
216
|
+
* makes swapping one for another a one-tag change (ADR 0016). A `<List>` with
|
|
217
|
+
* no recognised view child keeps the table default rather than rendering
|
|
218
|
+
* nothing, matching the behaviour before the board and grid views existed.
|
|
219
|
+
*/
|
|
220
|
+
export declare function listConfigFromChildren(listChildren: ReactNode): UdsAdminListConfig;
|