@hmlr/govuk-react-components-library 1.0.3 → 1.1.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.
package/dist/index.cjs.js CHANGED
@@ -92,10 +92,10 @@ function ExtractAccordionConfigFromAttributes(remainingAttributes) {
92
92
  return accordionConfig;
93
93
  }
94
94
 
95
- const Accordion = ({ headingLevel: HeadingLevel = "h2", items, className, id, ...attributes }) => {
95
+ const Accordion = ({ headingLevel: HeadingLevel = "h2", items = [], className, id, ...attributes }) => {
96
96
  const remainingAttributes = { ...attributes };
97
97
  ExtractAccordionConfigFromAttributes(remainingAttributes);
98
- return (jsxRuntime.jsx("div", { ...remainingAttributes, id: id, className: `govuk-accordion ${className || ""}`, "data-module": "govuk-accordion", children: items.filter(Boolean).map((item, index) => (jsxRuntime.jsxs("div", { className: `govuk-accordion__section ${item.expanded ? "govuk-accordion__section--expanded" : ""}`, children: [jsxRuntime.jsxs("div", { className: "govuk-accordion__section-header", children: [jsxRuntime.jsx(HeadingLevel, { className: "govuk-accordion__section-heading", children: jsxRuntime.jsx("span", { className: "govuk-accordion__section-button", id: `${id}-heading-${index + 1}`, children: item.heading.children }) }), item.summary && (jsxRuntime.jsx("div", { className: "govuk-accordion__section-summary govuk-body", id: `${id}-summary-${index + 1}`, children: item.summary.children }))] }), jsxRuntime.jsx("div", { id: `${id}-content-${index + 1}`, className: "govuk-accordion__section-content", "aria-labelledby": `${id}-heading-${index + 1}`, children: item.content.children })] }, item.reactListKey || index))) }));
98
+ return (jsxRuntime.jsx("div", { ...remainingAttributes, id: id, className: `govuk-accordion ${className || ""}`, "data-module": "govuk-accordion", children: items.filter(Boolean).map((item, index) => (jsxRuntime.jsxs("div", { className: `govuk-accordion__section ${item.expanded ? "govuk-accordion__section--expanded" : ""}`, children: [jsxRuntime.jsxs("div", { className: "govuk-accordion__section-header", children: [jsxRuntime.jsx(HeadingLevel, { className: "govuk-accordion__section-heading", children: jsxRuntime.jsx("span", { className: "govuk-accordion__section-button", id: `${id}-heading-${index + 1}`, children: item.heading?.children }) }), item.summary && (jsxRuntime.jsx("div", { className: "govuk-accordion__section-summary govuk-body", id: `${id}-summary-${index + 1}`, children: item.summary.children }))] }), jsxRuntime.jsx("div", { id: `${id}-content-${index + 1}`, className: "govuk-accordion__section-content", "aria-labelledby": `${id}-heading-${index + 1}`, children: item.content?.children })] }, item.reactListKey || index))) }));
99
99
  };
100
100
 
101
101
  const LinkWithRef = ({ children, to, href, forwardedRef = null, ...attributes }) => {
@@ -831,6 +831,24 @@ function ConfigureOverallErrorSummary($scope, config) {
831
831
  govukFrontend.createAll(govukFrontend.ErrorSummary, config, $scope);
832
832
  }
833
833
 
834
+ const ExitThisPage = ({ children = "Exit this page", className, redirectUrl = "https://www.bbc.co.uk/weather", activatedText, timedOutText, pressTwoMoreTimesText, pressOneMoreTimeText, ...attributes }) => {
835
+ const i18nProps = {};
836
+ if (activatedText)
837
+ i18nProps["data-i18n.activated"] = activatedText;
838
+ if (timedOutText)
839
+ i18nProps["data-i18n.timed-out"] = timedOutText;
840
+ if (pressTwoMoreTimesText)
841
+ i18nProps["data-i18n.press-two-more-times"] = pressTwoMoreTimesText;
842
+ if (pressOneMoreTimeText)
843
+ i18nProps["data-i18n.press-one-more-time"] = pressOneMoreTimeText;
844
+ return (jsxRuntime.jsx("div", { ...attributes, ...i18nProps, className: `govuk-exit-this-page${className ? ` ${className}` : ""}`, "data-module": "govuk-exit-this-page", children: jsxRuntime.jsxs("a", { href: redirectUrl, role: "button", draggable: false, className: "govuk-button govuk-button--warning govuk-exit-this-page__button govuk-js-exit-this-page-button", "data-module": "govuk-button", rel: "nofollow noreferrer", children: [jsxRuntime.jsx("span", { className: "govuk-visually-hidden", children: "Emergency" }), " ", children] }) }));
845
+ };
846
+ ExitThisPage.displayName = "ExitThisPage";
847
+
848
+ function ConfigureOverallExitThisPage($scope) {
849
+ govukFrontend.createAll(govukFrontend.ExitThisPage, {}, $scope);
850
+ }
851
+
834
852
  const FileUpload = (props) => {
835
853
  const { className, errorMessage, formGroup, hint, label, "aria-describedby": describedBy, id, ...attributes } = props;
836
854
  let hintComponent;
@@ -955,6 +973,17 @@ const NotificationBanner = (props) => {
955
973
  return (jsxRuntime.jsxs("div", { className: `govuk-notification-banner ${typeClass} ${className || ""}`, role: roleAttribute, "aria-labelledby": titleId, "data-module": "govuk-notification-banner", ...(disableAutoFocus ? { "data-disable-auto-focus": "true" } : {}), ...attributes, children: [jsxRuntime.jsx("div", { className: "govuk-notification-banner__header", children: jsxRuntime.jsx(HeadingLevel, { className: "govuk-notification-banner__title", id: titleId, children: title }) }), jsxRuntime.jsx("div", { className: "govuk-notification-banner__content", children: typeof children === "string" ? (jsxRuntime.jsx("p", { className: "govuk-notification-banner__heading", children: children })) : (children) })] }));
956
974
  };
957
975
 
976
+ const SvgIcon = ({ type, ...props }) => (jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: 15, height: 13, "aria-hidden": "true", ...props, children: jsxRuntime.jsx("path", { d: type === "next"
977
+ ? "M8.107-.008L6.693 1.406 10.986 5.7H-2v2h12.896L6.71 11.676l1.378 1.449 6.744-6.406L8.107-.008z"
978
+ : "M6.594-.008-.133 6.72l6.744 6.406 1.377-1.449-4.185-3.977h12.896v-2H3.715l4.293-4.293L6.594-.008z" }) }));
979
+
980
+ const NavigationButton = ({ href, type, children, labelText, }) => {
981
+ const defaultText = type === "next" ? "Next" : "Previous";
982
+ const typeClass = type === "next" ? "next" : "prev";
983
+ const icon = (jsxRuntime.jsx(SvgIcon, { type: type, className: `govuk-pagination__icon govuk-pagination__icon--${typeClass}`, focusable: "false", "aria-hidden": "true" }));
984
+ return (jsxRuntime.jsxs("a", { className: "govuk-link govuk-pagination__link", href: href, rel: type, children: [type === "previous" && icon, jsxRuntime.jsxs("span", { className: "govuk-pagination__link-title", children: [children ?? defaultText, jsxRuntime.jsx("span", { className: "govuk-visually-hidden", children: " page" })] }), type === "next" && icon, labelText && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { className: "govuk-visually-hidden", children: ":" }), jsxRuntime.jsx("span", { className: "govuk-pagination__link-label", children: labelText })] }))] }));
985
+ };
986
+
958
987
  const DOTS = "...";
959
988
  const UsePagination = ({ totalCount, pageSize, siblingCount = 1, currentPage, }) => {
960
989
  const range = (start, end) => {
@@ -990,29 +1019,13 @@ const UsePagination = ({ totalCount, pageSize, siblingCount = 1, currentPage, })
990
1019
  return paginationRange;
991
1020
  };
992
1021
 
993
- const SvgIcon = ({ type, ...props }) => (jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: 15, height: 13, "aria-hidden": "true", ...props, children: jsxRuntime.jsx("path", { d: type === "next"
994
- ? "M8.107-.008L6.693 1.406 10.986 5.7H-2v2h12.896L6.71 11.676l1.378 1.449 6.744-6.406L8.107-.008z"
995
- : "M6.594-.008-.133 6.72l6.744 6.406 1.377-1.449-4.185-3.977h12.896v-2H3.715l4.293-4.293L6.594-.008z" }) }));
996
-
997
- const NavigationButton = ({ isDisabled, onClick, children, name, type, }) => {
998
- const typeClass = type === "next" ? type : "prev";
999
- const Icon = (jsxRuntime.jsx(SvgIcon, { type: type, className: `govuk-pagination__icon govuk-pagination__icon--${typeClass}`, focusable: "false" }));
1000
- return isDisabled ? (jsxRuntime.jsxs("div", { rel: type, children: [type === "previous" && Icon, jsxRuntime.jsx("span", { className: "govuk-pagination__link-title", children: name }), type === "next" && Icon, children && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [type === "previous" && jsxRuntime.jsx(jsxRuntime.Fragment, { children: "\u00A0" }), jsxRuntime.jsx("span", { className: "govuk-visually-hidden", children: ":" }), children, type === "next" && jsxRuntime.jsx(jsxRuntime.Fragment, { children: " \u00A0" })] }))] })) : (jsxRuntime.jsxs("a", { className: "govuk-link govuk-pagination__link", href: "#", rel: type, onClick: onClick, children: [type === "previous" && Icon, jsxRuntime.jsx("span", { className: "govuk-pagination__link-title", children: name }), type === "next" && Icon, children && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [type === "previous" && jsxRuntime.jsx(jsxRuntime.Fragment, { children: "\u00A0\u00A0" }), jsxRuntime.jsx("span", { className: "govuk-visually-hidden", children: ":" }), children, type === "next" && jsxRuntime.jsx(jsxRuntime.Fragment, { children: "\u00A0\u00A0" })] }))] }));
1001
- };
1002
-
1003
- const Pagination = ({ onPageChange, totalCount, siblingCount = 1, currentPage, pageSize, className, previousName = "Previous", nextName = "Next", previousChildren, nextChildren, }) => {
1004
- const paginationRange = UsePagination({
1005
- currentPage,
1006
- totalCount,
1007
- siblingCount,
1008
- pageSize,
1009
- });
1010
- if (!paginationRange || paginationRange.length < 2)
1022
+ const Pagination = ({ previous, next, items, className, }) => {
1023
+ if (!previous && !next && (!items || items.length === 0))
1011
1024
  return null;
1012
- const onNext = () => onPageChange(currentPage + 1);
1013
- const onPrevious = () => onPageChange(currentPage - 1);
1014
- const lastPage = paginationRange[paginationRange.length - 1];
1015
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "govuk-!-padding-bottom-2" }), jsxRuntime.jsx("div", { className: `govuk-grid-column-full ${className || ""}`, children: jsxRuntime.jsxs("nav", { className: "govuk-pagination", role: "navigation", "aria-label": "Pagination", children: [jsxRuntime.jsx("div", { className: "govuk-pagination__prev", children: jsxRuntime.jsx(NavigationButton, { isDisabled: currentPage === 1, onClick: onPrevious, name: previousName, type: "previous", children: previousChildren }) }), jsxRuntime.jsx("ul", { className: "govuk-pagination__list", children: paginationRange.map((pageNumber, index) => pageNumber === DOTS ? (jsxRuntime.jsx("li", { className: "govuk-pagination__item govuk-pagination__item--ellipses", children: DOTS }, index)) : (jsxRuntime.jsx("li", { className: `govuk-pagination__item ${pageNumber === currentPage ? "govuk-pagination__item--current" : ""}`, children: jsxRuntime.jsx("a", { className: "govuk-link govuk-pagination__link", href: "#", "aria-label": `Page ${pageNumber}`, "aria-current": pageNumber === currentPage ? "page" : undefined, onClick: () => onPageChange(pageNumber), children: pageNumber }) }, index))) }), jsxRuntime.jsx("div", { className: "govuk-pagination__next", children: jsxRuntime.jsx(NavigationButton, { isDisabled: currentPage === lastPage, onClick: onNext, name: nextName, type: "next", children: nextChildren }) })] }) }), jsxRuntime.jsx("div", { className: "govuk-!-padding-bottom-4" })] }));
1025
+ // Block layout is used when there are no numbered page items — govuk renders
1026
+ // the nav with an extra class and the link title with --decorated modifier.
1027
+ const isBlockLayout = !items || items.length === 0;
1028
+ return (jsxRuntime.jsxs("nav", { className: `govuk-pagination${isBlockLayout ? " govuk-pagination--block" : ""}${className ? ` ${className}` : ""}`, role: "navigation", "aria-label": "Pagination", children: [previous && (jsxRuntime.jsx("div", { className: "govuk-pagination__prev", children: jsxRuntime.jsx(NavigationButton, { type: "previous", ...previous }) })), items && items.length > 0 && (jsxRuntime.jsx("ul", { className: "govuk-pagination__list", children: items.map((item, index) => item.ellipsis ? (jsxRuntime.jsx("li", { className: "govuk-pagination__item govuk-pagination__item--ellipses", children: DOTS }, index)) : (jsxRuntime.jsx("li", { className: `govuk-pagination__item${item.current ? " govuk-pagination__item--current" : ""}`, children: jsxRuntime.jsx("a", { className: "govuk-link govuk-pagination__link", href: item.href ?? "#", "aria-label": `Page ${item.number}`, ...(item.current ? { "aria-current": "page" } : {}), children: item.number }) }, index))) })), next && (jsxRuntime.jsx("div", { className: "govuk-pagination__next", children: jsxRuntime.jsx(NavigationButton, { type: "next", ...next }) }))] }));
1016
1029
  };
1017
1030
 
1018
1031
  const Panel = (props) => {
@@ -1021,6 +1034,49 @@ const Panel = (props) => {
1021
1034
  return (jsxRuntime.jsxs("div", { className: `govuk-panel govuk-panel--confirmation ${className || ""}`, ...attributes, children: [jsxRuntime.jsx(HeadingLevel, { className: "govuk-panel__title", children: titleChildren }), innerHtml] }));
1022
1035
  };
1023
1036
 
1037
+ const PasswordInput = React.forwardRef((props, ref) => {
1038
+ const { className, "aria-describedby": describedBy, errorMessage, formGroup, hint, label, name, id, autoComplete = "current-password", showPasswordText = "Show", hidePasswordText = "Hide", showPasswordAriaLabelText = "Show password", hidePasswordAriaLabelText = "Hide password", passwordShownAnnouncementText, passwordHiddenAnnouncementText, ...attributes } = props;
1039
+ let describedByValue = describedBy || "";
1040
+ let hintComponent = null;
1041
+ let errorMessageComponent = null;
1042
+ if (hint) {
1043
+ const hintId = `${id}-hint`;
1044
+ describedByValue += ` ${hintId}`;
1045
+ hintComponent = jsxRuntime.jsx(Hint, { ...hint, id: hintId });
1046
+ }
1047
+ if (errorMessage) {
1048
+ const errorId = id ? `${id}-error` : "";
1049
+ describedByValue += ` ${errorId}`;
1050
+ errorMessageComponent = jsxRuntime.jsx(ErrorMessage, { ...errorMessage, id: errorId });
1051
+ }
1052
+ // Build i18n data attributes for the govuk-frontend JS module
1053
+ const i18nProps = {};
1054
+ if (showPasswordText)
1055
+ i18nProps["data-i18n.show-password"] = showPasswordText;
1056
+ if (hidePasswordText)
1057
+ i18nProps["data-i18n.hide-password"] = hidePasswordText;
1058
+ if (showPasswordAriaLabelText)
1059
+ i18nProps["data-i18n.show-password-aria-label"] =
1060
+ showPasswordAriaLabelText;
1061
+ if (hidePasswordAriaLabelText)
1062
+ i18nProps["data-i18n.hide-password-aria-label"] =
1063
+ hidePasswordAriaLabelText;
1064
+ if (passwordShownAnnouncementText)
1065
+ i18nProps["data-i18n.password-shown-announcement"] =
1066
+ passwordShownAnnouncementText;
1067
+ if (passwordHiddenAnnouncementText)
1068
+ i18nProps["data-i18n.password-hidden-announcement"] =
1069
+ passwordHiddenAnnouncementText;
1070
+ // Label handles isPageHeading internally — no extra wrapper needed here
1071
+ const labelEl = jsxRuntime.jsx(Label, { ...label, htmlFor: id });
1072
+ return (jsxRuntime.jsxs("div", { className: `govuk-form-group${formGroup?.className ? ` ${formGroup.className}` : ""}${errorMessage ? " govuk-form-group--error" : ""} govuk-password-input`, "data-module": "govuk-password-input", ...i18nProps, children: [labelEl, hintComponent, errorMessageComponent, jsxRuntime.jsxs("div", { className: "govuk-input__wrapper govuk-password-input__wrapper", children: [jsxRuntime.jsx("input", { ref: ref, id: id, className: `govuk-input govuk-password-input__input govuk-js-password-input-input${className ? ` ${className}` : ""}${errorMessage ? " govuk-input--error" : ""}`, name: name || id, type: "password", spellCheck: false, autoComplete: autoComplete, autoCapitalize: "none", "aria-describedby": describedByValue.trim() || undefined, ...attributes }), jsxRuntime.jsx("button", { type: "button", className: "govuk-button govuk-button--secondary govuk-password-input__toggle govuk-js-password-input-toggle", "data-module": "govuk-button", "aria-controls": id, "aria-label": showPasswordAriaLabelText, hidden: true, children: showPasswordText })] })] }));
1073
+ });
1074
+ PasswordInput.displayName = "PasswordInput";
1075
+
1076
+ function ConfigureOverallPasswordInput($scope) {
1077
+ govukFrontend.createAll(govukFrontend.PasswordInput, {}, $scope);
1078
+ }
1079
+
1024
1080
  function Slugify(str) {
1025
1081
  str = str.replace(/^\s+|\s+$/g, ""); // trim leading/trailing white space
1026
1082
  str = str.toLowerCase(); // convert string to lowercase
@@ -1188,7 +1244,7 @@ const PDFViewer = (props) => {
1188
1244
  // 1) Let ResolvePDFSource handle base64/data/blob inputs synchronously.
1189
1245
  try {
1190
1246
  const resolved = ResolvePDFSource(src);
1191
- if (resolved && resolved.source) {
1247
+ if (resolved?.source) {
1192
1248
  sourceToUse = resolved.source;
1193
1249
  if (resolved.isBase64Source) {
1194
1250
  // ResolvePDFSource created an object URL for us (data/blob/base64)
@@ -1215,11 +1271,7 @@ const PDFViewer = (props) => {
1215
1271
  signal: controller.signal,
1216
1272
  credentials: "omit",
1217
1273
  });
1218
- if (!resp.ok) {
1219
- console.warn(`PDFViewer: fetch returned ${resp.status} ${resp.statusText} for ${src}`);
1220
- // fallback: keep using original src
1221
- }
1222
- else {
1274
+ if (resp.ok) {
1223
1275
  const blob = await resp.blob();
1224
1276
  if (blob && blob.size > 0) {
1225
1277
  const obj = URL.createObjectURL(blob);
@@ -1231,9 +1283,13 @@ const PDFViewer = (props) => {
1231
1283
  console.warn("PDFViewer: fetched blob is empty - falling back to original src");
1232
1284
  }
1233
1285
  }
1286
+ else {
1287
+ console.warn(`PDFViewer: fetch returned ${resp.status} ${resp.statusText} for ${src}`);
1288
+ // fallback: keep using original src
1289
+ }
1234
1290
  }
1235
1291
  catch (err) {
1236
- if (err?.name === "AbortError") {
1292
+ if (err instanceof Error && err.name === "AbortError") {
1237
1293
  // aborted, nothing to do
1238
1294
  if (!mounted)
1239
1295
  return;
@@ -1560,11 +1616,14 @@ exports.ConfigureOverallAccordion = ConfigureOverallAccordion;
1560
1616
  exports.ConfigureOverallButton = ConfigureOverallButton;
1561
1617
  exports.ConfigureOverallCheckboxes = ConfigureOverallCheckboxes;
1562
1618
  exports.ConfigureOverallErrorSummary = ConfigureOverallErrorSummary;
1619
+ exports.ConfigureOverallExitThisPage = ConfigureOverallExitThisPage;
1563
1620
  exports.ConfigureOverallHeader = ConfigureOverallHeader;
1621
+ exports.ConfigureOverallPasswordInput = ConfigureOverallPasswordInput;
1564
1622
  exports.ConfigureOverallRadios = ConfigureOverallRadios;
1565
1623
  exports.ConfigureOverallSkipLink = ConfigureOverallSkipLink;
1566
1624
  exports.ConfigureOverallTabs = ConfigureOverallTabs;
1567
1625
  exports.CookieBanner = CookieBanner;
1626
+ exports.DOTS = DOTS;
1568
1627
  exports.DataNavigation = DataNavigation;
1569
1628
  exports.DateInput = DateInput;
1570
1629
  exports.Details = Details;
@@ -1572,6 +1631,7 @@ exports.DifferenceNavigation = DifferenceNavigation;
1572
1631
  exports.ErrorBoundary = ErrorBoundary;
1573
1632
  exports.ErrorMessage = ErrorMessage;
1574
1633
  exports.ErrorSummary = ErrorSummary;
1634
+ exports.ExitThisPage = ExitThisPage;
1575
1635
  exports.ExtractAccordionConfigFromAttributes = ExtractAccordionConfigFromAttributes;
1576
1636
  exports.Fieldset = Fieldset;
1577
1637
  exports.FileUpload = FileUpload;
@@ -1591,6 +1651,7 @@ exports.PDFViewer = PDFViewer;
1591
1651
  exports.PDFViewerBackend = PDFViewerBackend;
1592
1652
  exports.Pagination = Pagination;
1593
1653
  exports.Panel = Panel;
1654
+ exports.PasswordInput = PasswordInput;
1594
1655
  exports.PhaseBanner = PhaseBanner;
1595
1656
  exports.ProblemWithService = ProblemWithService;
1596
1657
  exports.Radios = Radios;
@@ -1603,6 +1664,7 @@ exports.Tabs = Tabs;
1603
1664
  exports.Tag = Tag;
1604
1665
  exports.TaskList = TaskList;
1605
1666
  exports.Textarea = Textarea;
1667
+ exports.UsePagination = UsePagination;
1606
1668
  exports.WarningInfo = WarningInfo;
1607
1669
  exports.WarningText = WarningText;
1608
1670
  //# sourceMappingURL=index.cjs.js.map