@uktrade/react-component-library 0.12.0 → 0.14.0

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.
Files changed (27) hide show
  1. package/dist/components/ApiQuery/ApiCreateHooks.d.ts +2 -2
  2. package/dist/components/ApiQuery/ApiCreateHooks.d.ts.map +1 -1
  3. package/dist/components/ApiQuery/ApiCreateHooks.js +18 -14
  4. package/dist/components/Inputs/CheckBoxesInput/CheckBoxGroupInput.d.ts +22 -0
  5. package/dist/components/Inputs/CheckBoxesInput/CheckBoxGroupInput.d.ts.map +1 -0
  6. package/dist/components/Inputs/CheckBoxesInput/CheckBoxGroupInput.js +16 -0
  7. package/dist/components/Inputs/SelectInput/SelectInput.d.ts +21 -0
  8. package/dist/components/Inputs/SelectInput/SelectInput.d.ts.map +1 -0
  9. package/dist/components/Inputs/SelectInput/SelectInput.js +18 -0
  10. package/dist/components/Inputs/TextInput/TextInput.d.ts +17 -0
  11. package/dist/components/Inputs/TextInput/TextInput.d.ts.map +1 -0
  12. package/dist/components/Inputs/TextInput/TextInput.js +19 -0
  13. package/dist/components/Inputs/index.d.ts +4 -0
  14. package/dist/components/Inputs/index.d.ts.map +1 -0
  15. package/dist/components/Inputs/index.js +3 -0
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +1 -0
  19. package/package.json +1 -1
  20. package/src/components/ApiQuery/ApiCreateHooks.tsx +18 -14
  21. package/src/components/Inputs/CheckBoxesInput/CheckBoxGroupInput.tsx +100 -0
  22. package/src/components/Inputs/DateInput/.gitkeep +0 -0
  23. package/src/components/Inputs/RadioInput/.gitkeep +0 -0
  24. package/src/components/Inputs/SelectInput/SelectInput.tsx +102 -0
  25. package/src/components/Inputs/TextInput/TextInput.tsx +105 -0
  26. package/src/components/Inputs/index.ts +3 -0
  27. package/src/index.ts +1 -0
@@ -1,7 +1,7 @@
1
1
  import type { PathsWithMethod } from "openapi-typescript-helpers";
2
2
  import { createApiClient } from "./ApiCreateClient";
3
3
  export declare function createApiHooks<Paths extends {}>(client: ReturnType<typeof createApiClient<Paths>>): {
4
- get: <P extends PathsWithMethod<Paths, "get">>(path: P, params?: any) => {
4
+ get: <P extends PathsWithMethod<Paths, "get">>(path: P, input?: any) => {
5
5
  query: import("@tanstack/react-query").UseQueryResult<any, Error>;
6
6
  state: {
7
7
  data: any;
@@ -11,7 +11,7 @@ export declare function createApiHooks<Paths extends {}>(client: ReturnType<type
11
11
  refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<any, Error>>;
12
12
  };
13
13
  };
14
- post: <P extends PathsWithMethod<Paths, "post">>(path: P, params?: any) => {
14
+ post: <P extends PathsWithMethod<Paths, "post">>(path: P, input?: any) => {
15
15
  query: import("@tanstack/react-query").UseQueryResult<any, Error>;
16
16
  state: {
17
17
  data: any;
@@ -1 +1 @@
1
- {"version":3,"file":"ApiCreateHooks.d.ts","sourceRoot":"","sources":["../../../src/components/ApiQuery/ApiCreateHooks.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAElE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD,wBAAgB,cAAc,CAAC,KAAK,SAAS,EAAE,EAC7C,MAAM,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;UAOzC,CAAC,SAAS,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,QACrC,CAAC,WACE,GAAG;;;;;;;;;;WAYP,CAAC,SAAS,eAAe,CAAC,KAAK,EAAE,MAAM,CAAC,QACvC,CAAC,WACE,GAAG;;;;;;;;;;mBAYC,CAAC,SAAS,eAAe,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC;;;;;;;;;;kBAQlD,CAAC,SAAS,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC;;;;;;;;;;oBAQ9C,CAAC,SAAS,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC;;;;;;;;;;qBAQjD,CAAC,SAAS,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC;;;;;;;;;;EAQvE"}
1
+ {"version":3,"file":"ApiCreateHooks.d.ts","sourceRoot":"","sources":["../../../src/components/ApiQuery/ApiCreateHooks.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAElE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAKpD,wBAAgB,cAAc,CAAC,KAAK,SAAS,EAAE,EAC7C,MAAM,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;UAQzC,CAAC,SAAS,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,QACrC,CAAC,UACC,GAAG;;;;;;;;;;WAYN,CAAC,SAAS,eAAe,CAAC,KAAK,EAAE,MAAM,CAAC,QACvC,CAAC,UACC,GAAG;;;;;;;;;;mBAaE,CAAC,SAAS,eAAe,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC;;;;;;;;;;kBAQlD,CAAC,SAAS,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC;;;;;;;;;;oBAQ9C,CAAC,SAAS,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC;;;;;;;;;;qBAQjD,CAAC,SAAS,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC;;;;;;;;;;EAQvE"}
@@ -1,54 +1,58 @@
1
1
  "use client";
2
2
  import { createApiClient } from "./ApiCreateClient";
3
3
  import { useApi, useApiMutation } from "./ApiProvider";
4
+ // API hook factory that converts a typed OpenAPI client into React hooks (`useApi` and `useApiMutation`) that the UI can use.
5
+ // Provides a simple interface for making API calls and handling loading and error states in your React components.
4
6
  export function createApiHooks(client) {
5
7
  // ToDo: Generalize this (params?: any) and fight openapi-fetch’s internal generics to get correct types from PathsWithMethod. Same for the mutation methods.
6
8
  // type GetParams<P extends PathsWithMethod<Paths, "get">> =
7
9
  // Parameters<typeof client.GET<P>>[1];
10
+ // Using useQuery
8
11
  return {
9
- get: (path, params) => {
10
- return useApi(["get", path, JSON.stringify(params ?? {})], async () => {
11
- const res = await client.GET(path, params);
12
+ get: (path, input) => {
13
+ return useApi(["get", path, JSON.stringify(input ?? {})], async () => {
14
+ const res = await client.GET(path, input);
12
15
  if (res.error)
13
16
  throw res.error;
14
17
  return res.data;
15
18
  });
16
19
  },
17
- post: (path, params) => {
18
- return useApi(["post", path, JSON.stringify(params ?? {})], async () => {
19
- const res = await client.POST(path, params);
20
+ post: (path, input) => {
21
+ return useApi(["post", path, JSON.stringify(input ?? {})], async () => {
22
+ const res = await client.POST(path, input);
20
23
  if (res.error)
21
24
  throw res.error;
22
25
  return res.data;
23
26
  });
24
27
  },
28
+ // Using useMutation
25
29
  postMutation: (path) => {
26
- return useApiMutation(async (params) => {
27
- const res = await client.POST(path, params);
30
+ return useApiMutation(async (input) => {
31
+ const res = await client.POST(path, input);
28
32
  if (res.error)
29
33
  throw res.error;
30
34
  return res.data;
31
35
  });
32
36
  },
33
37
  putMutation: (path) => {
34
- return useApiMutation(async (params) => {
35
- const res = await client.PUT(path, params);
38
+ return useApiMutation(async (input) => {
39
+ const res = await client.PUT(path, input);
36
40
  if (res.error)
37
41
  throw res.error;
38
42
  return res.data;
39
43
  });
40
44
  },
41
45
  patchMutation: (path) => {
42
- return useApiMutation(async (params) => {
43
- const res = await client.PATCH(path, params);
46
+ return useApiMutation(async (input) => {
47
+ const res = await client.PATCH(path, input);
44
48
  if (res.error)
45
49
  throw res.error;
46
50
  return res.data;
47
51
  });
48
52
  },
49
53
  deleteMutation: (path) => {
50
- return useApiMutation(async (params) => {
51
- const res = await client.DELETE(path, params);
54
+ return useApiMutation(async (input) => {
55
+ const res = await client.DELETE(path, input);
52
56
  if (res.error)
53
57
  throw res.error;
54
58
  return res.data;
@@ -0,0 +1,22 @@
1
+ import type { ReactNode } from "react";
2
+ type LegendAs = "h1" | "h2" | "h3" | "h4";
3
+ type LegendSize = "s" | "m" | "l" | "xl";
4
+ interface CheckboxOption {
5
+ id: string;
6
+ value: string;
7
+ label: ReactNode;
8
+ checked?: boolean;
9
+ }
10
+ interface CheckboxGroupProps {
11
+ legend: ReactNode;
12
+ hint?: ReactNode;
13
+ error?: string;
14
+ options: CheckboxOption[];
15
+ name: string;
16
+ legendAs?: LegendAs;
17
+ legendSize?: LegendSize;
18
+ checkboxSize?: "small" | "default";
19
+ }
20
+ export declare const CheckboxGroup: ({ legend, hint, error, options, name, legendAs, legendSize, checkboxSize }: CheckboxGroupProps) => import("react/jsx-runtime").JSX.Element;
21
+ export {};
22
+ //# sourceMappingURL=CheckBoxGroupInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CheckBoxGroupInput.d.ts","sourceRoot":"","sources":["../../../../src/components/Inputs/CheckBoxesInput/CheckBoxGroupInput.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,KAAK,QAAQ,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAC1C,KAAK,UAAU,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC;AAEzC,UAAU,cAAc;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,UAAU,kBAAkB;IACxB,MAAM,EAAE,SAAS,CAAC;IAClB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACtC;AAED,eAAO,MAAM,aAAa,GAAI,4EAS3B,kBAAkB,4CA+DpB,CAAC"}
@@ -0,0 +1,16 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import clsx from "clsx";
4
+ import { createElement } from "react";
5
+ export const CheckboxGroup = ({ legend, hint, error, options, name, legendAs = "h4", legendSize = "m", checkboxSize = "default" }) => {
6
+ const hasError = Boolean(error);
7
+ const errorId = hasError ? `${name}-error` : undefined;
8
+ const legendHeading = createElement(legendAs, { className: "govuk-fieldset__heading" }, legend);
9
+ return (_jsx("div", { className: clsx("govuk-form-group", {
10
+ "govuk-form-group--error": hasError,
11
+ }), children: _jsxs("fieldset", { className: "govuk-fieldset", "aria-describedby": errorId, children: [_jsx("legend", { className: clsx("govuk-fieldset__legend", {
12
+ [`govuk-fieldset__legend--${legendSize}`]: legendSize !== "m",
13
+ }), children: legendHeading }), hint && _jsx("div", { className: "govuk-hint", children: hint }), hasError && (_jsxs("p", { id: errorId, className: "govuk-error-message", children: [_jsx("span", { className: "govuk-visually-hidden", children: "Error:" }), " ", error] })), _jsx("div", { className: clsx("govuk-checkboxes", {
14
+ "govuk-checkboxes--small": checkboxSize === "small",
15
+ }), "data-module": "govuk-checkboxes", children: options.map((opt) => (_jsxs("div", { className: "govuk-checkboxes__item", children: [_jsx("input", { className: "govuk-checkboxes__input", id: opt.id, name: name, type: "checkbox", value: opt.value, defaultChecked: opt.checked }), _jsx("label", { className: "govuk-label govuk-checkboxes__label", htmlFor: opt.id, children: opt.label })] }, opt.id))) })] }) }));
16
+ };
@@ -0,0 +1,21 @@
1
+ import type { JSX, ReactNode } from "react";
2
+ type LabelAs = "h1" | "h2" | "h3" | "h4";
3
+ type LabelSize = "s" | "m" | "l" | "xl";
4
+ type SelectProps = Omit<JSX.IntrinsicElements["select"], "id">;
5
+ interface SelectInputProps extends SelectProps {
6
+ id: string;
7
+ label: ReactNode;
8
+ hint?: ReactNode;
9
+ error?: string;
10
+ options: {
11
+ value: string;
12
+ label: string;
13
+ }[];
14
+ placeholder?: string;
15
+ labelAs?: LabelAs;
16
+ labelSize?: LabelSize;
17
+ className?: string;
18
+ }
19
+ export declare const SelectInput: ({ id, label, hint, error, options, placeholder, labelAs, labelSize, className, ...props }: SelectInputProps) => import("react/jsx-runtime").JSX.Element;
20
+ export {};
21
+ //# sourceMappingURL=SelectInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SelectInput.d.ts","sourceRoot":"","sources":["../../../../src/components/Inputs/SelectInput/SelectInput.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAG5C,KAAK,OAAO,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACzC,KAAK,SAAS,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC;AACxC,KAAK,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC;AAE/D,UAAU,gBAAiB,SAAQ,WAAW;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,SAAS,CAAC;IACjB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,WAAW,GAAI,2FAWzB,gBAAgB,4CAoElB,CAAC"}
@@ -0,0 +1,18 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import clsx from "clsx";
4
+ import { createElement } from "react";
5
+ export const SelectInput = ({ id, label, hint, error, options, placeholder, labelAs, labelSize = "m", className, ...props }) => {
6
+ const hasError = Boolean(error);
7
+ const hintId = hint ? `${id}-hint` : undefined;
8
+ const errorId = hasError ? `${id}-error` : undefined;
9
+ const ariaDescribedBy = [hintId, errorId].filter(Boolean).join(" ") || undefined;
10
+ const labelElement = (_jsx("label", { htmlFor: id, className: clsx("govuk-label", {
11
+ [`govuk-label--${labelSize}`]: labelSize !== "m",
12
+ }), children: label }));
13
+ return (_jsxs("div", { className: clsx("govuk-form-group", {
14
+ "govuk-form-group--error": hasError,
15
+ }), children: [labelAs
16
+ ? createElement(labelAs, { className: "govuk-label-wrapper" }, labelElement)
17
+ : labelElement, hint && (_jsx("div", { id: hintId, className: "govuk-hint", children: hint })), hasError && (_jsxs("p", { id: errorId, className: "govuk-error-message", children: [_jsx("span", { className: "govuk-visually-hidden", children: "Error:" }), " ", error] })), _jsxs("select", { id: id, className: clsx("govuk-select", { "govuk-select--error": hasError }, className), "aria-describedby": ariaDescribedBy, ...props, children: [placeholder && (_jsx("option", { value: "", disabled: true, hidden: true, children: placeholder })), options.map((opt) => (_jsx("option", { value: opt.value, children: opt.label }, opt.value)))] })] }));
18
+ };
@@ -0,0 +1,17 @@
1
+ import type { JSX, ReactNode } from "react";
2
+ type LabelAs = "h1" | "h2" | "h3" | "h4";
3
+ type InputProps = Omit<JSX.IntrinsicElements["input"], "id">;
4
+ interface TextInputProps extends InputProps {
5
+ id: string;
6
+ label: ReactNode;
7
+ hint?: ReactNode;
8
+ error?: string;
9
+ width?: "10" | "20" | "30";
10
+ labelSize?: "s" | "m" | "l" | "xl";
11
+ labelAs?: LabelAs;
12
+ placeholder?: string;
13
+ suffix?: ReactNode;
14
+ }
15
+ export declare const TextInput: ({ id, label, hint, error, width, labelSize, labelAs, className, placeholder, suffix, ...props }: TextInputProps) => import("react/jsx-runtime").JSX.Element;
16
+ export {};
17
+ //# sourceMappingURL=TextInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TextInput.d.ts","sourceRoot":"","sources":["../../../../src/components/Inputs/TextInput/TextInput.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAG5C,KAAK,OAAO,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACzC,KAAK,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;AAE7D,UAAU,cAAe,SAAQ,UAAU;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,SAAS,CAAC;IACjB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC3B,SAAS,CAAC,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC;IACnC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,eAAO,MAAM,SAAS,GAAI,iGAYvB,cAAc,4CAuEhB,CAAC"}
@@ -0,0 +1,19 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import clsx from "clsx";
4
+ import { createElement } from "react";
5
+ export const TextInput = ({ id, label, hint, error, width = "20", labelSize = "m", labelAs, className, placeholder, suffix, ...props }) => {
6
+ const hasError = Boolean(error);
7
+ const hintId = hint ? `${id}-hint` : undefined;
8
+ const errorId = hasError ? `${id}-error` : undefined;
9
+ const describedBy = [hintId, errorId].filter(Boolean).join(" ") || undefined;
10
+ const labelElement = (_jsx("label", { htmlFor: id, className: clsx("govuk-label", {
11
+ [`govuk-label--${labelSize}`]: labelSize !== "m",
12
+ }), children: label }));
13
+ const input = (_jsx("input", { id: id, className: clsx("govuk-input", `govuk-input--width-${width}`, className), "aria-invalid": hasError, "aria-describedby": describedBy, placeholder: placeholder, ...props }));
14
+ return (_jsxs("div", { className: clsx("govuk-form-group", {
15
+ "govuk-form-group--error": hasError,
16
+ }), children: [labelAs
17
+ ? createElement(labelAs, { className: "govuk-label-wrapper" }, labelElement)
18
+ : labelElement, hint && (_jsx("div", { id: hintId, className: "govuk-hint", children: hint })), hasError && (_jsxs("p", { id: errorId, className: "govuk-error-message", children: [_jsx("span", { className: "govuk-visually-hidden", children: "Error:" }), " ", error] })), suffix ? (_jsxs("div", { className: "govuk-input__wrapper", children: [input, _jsx("div", { className: "govuk-input__suffix", "aria-hidden": "true", children: suffix })] })) : (input)] }));
19
+ };
@@ -0,0 +1,4 @@
1
+ export * from "./TextInput/TextInput";
2
+ export * from "./CheckBoxesInput/CheckBoxGroupInput";
3
+ export * from "./SelectInput/SelectInput";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Inputs/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,sCAAsC,CAAC;AACrD,cAAc,2BAA2B,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from "./TextInput/TextInput";
2
+ export * from "./CheckBoxesInput/CheckBoxGroupInput";
3
+ export * from "./SelectInput/SelectInput";
package/dist/index.d.ts CHANGED
@@ -11,4 +11,5 @@ export * from "./components/BackLink";
11
11
  export * from "./components/LoadingSpinner/LoadingSpinner";
12
12
  export * from "./components/ApiBoundary/ApiBoundary";
13
13
  export * from "./components/ApiQuery";
14
+ export * from "./components/Inputs";
14
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sCAAsC,CAAC;AACrD,cAAc,sCAAsC,CAAC;AACrD,cAAc,wCAAwC,CAAC;AACvD,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,4CAA4C,CAAC;AAC3D,cAAc,sCAAsC,CAAC;AACrD,cAAc,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sCAAsC,CAAC;AACrD,cAAc,sCAAsC,CAAC;AACrD,cAAc,wCAAwC,CAAC;AACvD,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,4CAA4C,CAAC;AAC3D,cAAc,sCAAsC,CAAC;AACrD,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -11,3 +11,4 @@ export * from "./components/BackLink";
11
11
  export * from "./components/LoadingSpinner/LoadingSpinner";
12
12
  export * from "./components/ApiBoundary/ApiBoundary";
13
13
  export * from "./components/ApiQuery";
14
+ export * from "./components/Inputs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uktrade/react-component-library",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "A collection of reusable React components following GOV.UK design patterns.",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -5,6 +5,8 @@ import type { PathsWithMethod } from "openapi-typescript-helpers";
5
5
  import { createApiClient } from "./ApiCreateClient";
6
6
  import { useApi, useApiMutation } from "./ApiProvider";
7
7
 
8
+ // API hook factory that converts a typed OpenAPI client into React hooks (`useApi` and `useApiMutation`) that the UI can use.
9
+ // Provides a simple interface for making API calls and handling loading and error states in your React components.
8
10
  export function createApiHooks<Paths extends {}>(
9
11
  client: ReturnType<typeof createApiClient<Paths>>
10
12
  ) {
@@ -12,15 +14,16 @@ export function createApiHooks<Paths extends {}>(
12
14
  // type GetParams<P extends PathsWithMethod<Paths, "get">> =
13
15
  // Parameters<typeof client.GET<P>>[1];
14
16
 
17
+ // Using useQuery
15
18
  return {
16
19
  get: <P extends PathsWithMethod<Paths, "get">>(
17
20
  path: P,
18
- params?: any
21
+ input?: any
19
22
  ) => {
20
23
  return useApi(
21
- ["get", path, JSON.stringify(params ?? {})],
24
+ ["get", path, JSON.stringify(input ?? {})],
22
25
  async () => {
23
- const res = await client.GET(path, params);
26
+ const res = await client.GET(path, input);
24
27
  if (res.error) throw res.error;
25
28
  return res.data;
26
29
  }
@@ -29,45 +32,46 @@ export function createApiHooks<Paths extends {}>(
29
32
 
30
33
  post: <P extends PathsWithMethod<Paths, "post">>(
31
34
  path: P,
32
- params?: any
35
+ input?: any
33
36
  ) => {
34
37
  return useApi(
35
- ["post", path, JSON.stringify(params ?? {})],
38
+ ["post", path, JSON.stringify(input ?? {})],
36
39
  async () => {
37
- const res = await client.POST(path, params);
40
+ const res = await client.POST(path, input);
38
41
  if (res.error) throw res.error;
39
42
  return res.data;
40
43
  }
41
44
  );
42
45
  },
43
46
 
47
+ // Using useMutation
44
48
  postMutation: <P extends PathsWithMethod<Paths, "post">>(path: P) => {
45
- return useApiMutation(async (params: any) => {
46
- const res = await client.POST(path, params);
49
+ return useApiMutation(async (input: any) => {
50
+ const res = await client.POST(path, input);
47
51
  if (res.error) throw res.error;
48
52
  return res.data;
49
53
  });
50
54
  },
51
55
 
52
56
  putMutation: <P extends PathsWithMethod<Paths, "put">>(path: P) => {
53
- return useApiMutation(async (params: any) => {
54
- const res = await client.PUT(path, params);
57
+ return useApiMutation(async (input: any) => {
58
+ const res = await client.PUT(path, input);
55
59
  if (res.error) throw res.error;
56
60
  return res.data;
57
61
  });
58
62
  },
59
63
 
60
64
  patchMutation: <P extends PathsWithMethod<Paths, "patch">>(path: P) => {
61
- return useApiMutation(async (params: any) => {
62
- const res = await client.PATCH(path, params);
65
+ return useApiMutation(async (input: any) => {
66
+ const res = await client.PATCH(path, input);
63
67
  if (res.error) throw res.error;
64
68
  return res.data;
65
69
  });
66
70
  },
67
71
 
68
72
  deleteMutation: <P extends PathsWithMethod<Paths, "delete">>(path: P) => {
69
- return useApiMutation(async (params: any) => {
70
- const res = await client.DELETE(path, params);
73
+ return useApiMutation(async (input: any) => {
74
+ const res = await client.DELETE(path, input);
71
75
  if (res.error) throw res.error;
72
76
  return res.data;
73
77
  });
@@ -0,0 +1,100 @@
1
+ "use client";
2
+
3
+ import clsx from "clsx";
4
+ import type { ReactNode } from "react";
5
+ import { createElement } from "react";
6
+
7
+ type LegendAs = "h1" | "h2" | "h3" | "h4";
8
+ type LegendSize = "s" | "m" | "l" | "xl";
9
+
10
+ interface CheckboxOption {
11
+ id: string;
12
+ value: string;
13
+ label: ReactNode;
14
+ checked?: boolean;
15
+ }
16
+
17
+ interface CheckboxGroupProps {
18
+ legend: ReactNode;
19
+ hint?: ReactNode;
20
+ error?: string;
21
+ options: CheckboxOption[];
22
+ name: string;
23
+ legendAs?: LegendAs;
24
+ legendSize?: LegendSize;
25
+ checkboxSize?: "small" | "default";
26
+ }
27
+
28
+ export const CheckboxGroup = ({
29
+ legend,
30
+ hint,
31
+ error,
32
+ options,
33
+ name,
34
+ legendAs = "h4",
35
+ legendSize = "m",
36
+ checkboxSize = "default"
37
+ }: CheckboxGroupProps) => {
38
+ const hasError = Boolean(error);
39
+ const errorId = hasError ? `${name}-error` : undefined;
40
+
41
+ const legendHeading = createElement(
42
+ legendAs,
43
+ { className: "govuk-fieldset__heading" },
44
+ legend
45
+ );
46
+
47
+ return (
48
+ <div
49
+ className={clsx("govuk-form-group", {
50
+ "govuk-form-group--error": hasError,
51
+ })}
52
+ >
53
+ <fieldset className="govuk-fieldset" aria-describedby={errorId}>
54
+ <legend
55
+ className={clsx("govuk-fieldset__legend", {
56
+ [`govuk-fieldset__legend--${legendSize}`]:
57
+ legendSize !== "m",
58
+ })}
59
+ >
60
+ {legendHeading}
61
+ </legend>
62
+
63
+ {hint && <div className="govuk-hint">{hint}</div>}
64
+
65
+ {hasError && (
66
+ <p id={errorId} className="govuk-error-message">
67
+ <span className="govuk-visually-hidden">Error:</span>{" "}
68
+ {error}
69
+ </p>
70
+ )}
71
+
72
+ <div
73
+ className={clsx("govuk-checkboxes", {
74
+ "govuk-checkboxes--small": checkboxSize === "small",
75
+ })}
76
+ data-module="govuk-checkboxes"
77
+ >
78
+ {options.map((opt) => (
79
+ <div key={opt.id} className="govuk-checkboxes__item">
80
+ <input
81
+ className="govuk-checkboxes__input"
82
+ id={opt.id}
83
+ name={name}
84
+ type="checkbox"
85
+ value={opt.value}
86
+ defaultChecked={opt.checked}
87
+ />
88
+ <label
89
+ className="govuk-label govuk-checkboxes__label"
90
+ htmlFor={opt.id}
91
+ >
92
+ {opt.label}
93
+ </label>
94
+ </div>
95
+ ))}
96
+ </div>
97
+ </fieldset>
98
+ </div>
99
+ );
100
+ };
File without changes
File without changes
@@ -0,0 +1,102 @@
1
+ "use client";
2
+
3
+ import clsx from "clsx";
4
+ import type { JSX, ReactNode } from "react";
5
+ import { createElement } from "react";
6
+
7
+ type LabelAs = "h1" | "h2" | "h3" | "h4";
8
+ type LabelSize = "s" | "m" | "l" | "xl";
9
+ type SelectProps = Omit<JSX.IntrinsicElements["select"], "id">;
10
+
11
+ interface SelectInputProps extends SelectProps {
12
+ id: string;
13
+ label: ReactNode;
14
+ hint?: ReactNode;
15
+ error?: string;
16
+ options: { value: string; label: string }[];
17
+ placeholder?: string;
18
+ labelAs?: LabelAs;
19
+ labelSize?: LabelSize;
20
+ className?: string;
21
+ }
22
+
23
+ export const SelectInput = ({
24
+ id,
25
+ label,
26
+ hint,
27
+ error,
28
+ options,
29
+ placeholder,
30
+ labelAs,
31
+ labelSize = "m",
32
+ className,
33
+ ...props
34
+ }: SelectInputProps) => {
35
+ const hasError = Boolean(error);
36
+ const hintId = hint ? `${id}-hint` : undefined;
37
+ const errorId = hasError ? `${id}-error` : undefined;
38
+ const ariaDescribedBy =
39
+ [hintId, errorId].filter(Boolean).join(" ") || undefined;
40
+
41
+ const labelElement = (
42
+ <label
43
+ htmlFor={id}
44
+ className={clsx("govuk-label", {
45
+ [`govuk-label--${labelSize}`]: labelSize !== "m",
46
+ })}
47
+ >
48
+ {label}
49
+ </label>
50
+ );
51
+
52
+ return (
53
+ <div
54
+ className={clsx("govuk-form-group", {
55
+ "govuk-form-group--error": hasError,
56
+ })}
57
+ >
58
+ {labelAs
59
+ ? createElement(
60
+ labelAs,
61
+ { className: "govuk-label-wrapper" },
62
+ labelElement
63
+ )
64
+ : labelElement}
65
+
66
+ {hint && (
67
+ <div id={hintId} className="govuk-hint">
68
+ {hint}
69
+ </div>
70
+ )}
71
+
72
+ {hasError && (
73
+ <p id={errorId} className="govuk-error-message">
74
+ <span className="govuk-visually-hidden">Error:</span> {error}
75
+ </p>
76
+ )}
77
+
78
+ <select
79
+ id={id}
80
+ className={clsx(
81
+ "govuk-select",
82
+ { "govuk-select--error": hasError },
83
+ className
84
+ )}
85
+ aria-describedby={ariaDescribedBy}
86
+ {...props}
87
+ >
88
+ {placeholder && (
89
+ <option value="" disabled hidden>
90
+ {placeholder}
91
+ </option>
92
+ )}
93
+
94
+ {options.map((opt) => (
95
+ <option key={opt.value} value={opt.value}>
96
+ {opt.label}
97
+ </option>
98
+ ))}
99
+ </select>
100
+ </div>
101
+ );
102
+ };
@@ -0,0 +1,105 @@
1
+ "use client";
2
+
3
+ import clsx from "clsx";
4
+ import type { JSX, ReactNode } from "react";
5
+ import { createElement } from "react";
6
+
7
+ type LabelAs = "h1" | "h2" | "h3" | "h4";
8
+ type InputProps = Omit<JSX.IntrinsicElements["input"], "id">;
9
+
10
+ interface TextInputProps extends InputProps {
11
+ id: string;
12
+ label: ReactNode;
13
+ hint?: ReactNode;
14
+ error?: string;
15
+ width?: "10" | "20" | "30";
16
+ labelSize?: "s" | "m" | "l" | "xl";
17
+ labelAs?: LabelAs;
18
+ placeholder?: string;
19
+ suffix?: ReactNode;
20
+ }
21
+
22
+ export const TextInput = ({
23
+ id,
24
+ label,
25
+ hint,
26
+ error,
27
+ width = "20",
28
+ labelSize = "m",
29
+ labelAs,
30
+ className,
31
+ placeholder,
32
+ suffix,
33
+ ...props
34
+ }: TextInputProps) => {
35
+ const hasError = Boolean(error);
36
+
37
+ const hintId = hint ? `${id}-hint` : undefined;
38
+ const errorId = hasError ? `${id}-error` : undefined;
39
+ const describedBy = [hintId, errorId].filter(Boolean).join(" ") || undefined;
40
+
41
+ const labelElement = (
42
+ <label
43
+ htmlFor={id}
44
+ className={clsx("govuk-label", {
45
+ [`govuk-label--${labelSize}`]: labelSize !== "m",
46
+ })}
47
+ >
48
+ {label}
49
+ </label>
50
+ );
51
+
52
+ const input = (
53
+ <input
54
+ id={id}
55
+ className={clsx(
56
+ "govuk-input",
57
+ `govuk-input--width-${width}`,
58
+ className
59
+ )}
60
+ aria-invalid={hasError}
61
+ aria-describedby={describedBy}
62
+ placeholder={placeholder}
63
+ {...props}
64
+ />
65
+ );
66
+
67
+ return (
68
+ <div
69
+ className={clsx("govuk-form-group", {
70
+ "govuk-form-group--error": hasError,
71
+ })}
72
+ >
73
+ {labelAs
74
+ ? createElement(
75
+ labelAs,
76
+ { className: "govuk-label-wrapper" },
77
+ labelElement
78
+ )
79
+ : labelElement}
80
+
81
+ {hint && (
82
+ <div id={hintId} className="govuk-hint">
83
+ {hint}
84
+ </div>
85
+ )}
86
+
87
+ {hasError && (
88
+ <p id={errorId} className="govuk-error-message">
89
+ <span className="govuk-visually-hidden">Error:</span> {error}
90
+ </p>
91
+ )}
92
+
93
+ {suffix ? (
94
+ <div className="govuk-input__wrapper">
95
+ {input}
96
+ <div className="govuk-input__suffix" aria-hidden="true">
97
+ {suffix}
98
+ </div>
99
+ </div>
100
+ ) : (
101
+ input
102
+ )}
103
+ </div>
104
+ );
105
+ };
@@ -0,0 +1,3 @@
1
+ export * from "./TextInput/TextInput";
2
+ export * from "./CheckBoxesInput/CheckBoxGroupInput";
3
+ export * from "./SelectInput/SelectInput";
package/src/index.ts CHANGED
@@ -11,3 +11,4 @@ export * from "./components/BackLink";
11
11
  export * from "./components/LoadingSpinner/LoadingSpinner";
12
12
  export * from "./components/ApiBoundary/ApiBoundary";
13
13
  export * from "./components/ApiQuery";
14
+ export * from "./components/Inputs";