@rh-support/components 2.5.226 → 2.5.230

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.
@@ -1 +1 @@
1
- {"version":3,"file":"CaseContactsSelectorExternal.d.ts","sourceRoot":"","sources":["../../../src/Functional/CaseContactsSelectorExternal.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,6BAA6B,EAAE,MAAM,yCAAyC,CAAC;AACxF,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAE,MAAM,wCAAwC,CAAC;AAClE,OAAO,EAAuB,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAcxD,OAAO,KAA4D,MAAM,OAAO,CAAC;AAGjF,OAAO,EAAE,+BAA+B,EAAE,MAAM,wCAAwC,CAAC;AAEzF,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,0BAA0B,CAAC;AACzE,UAAU,MAAO,SAAQ,gBAAgB;IACrC,qBAAqB,EAAE,MAAM,CAAC;IAC9B,QAAQ,EAAE,CAAC,QAAQ,GAAG,0BAA0B,CAAC,EAAE,CAAC;IACpD,QAAQ,EAAE,CAAC,gBAAgB,EAAE,CAAC,QAAQ,GAAG,0BAA0B,CAAC,EAAE,KAAK,IAAI,CAAC;IAChF,kBAAkB,EAAE,QAAQ,CAAC;IAC7B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,QAAQ,EAAE,CAAC;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,CAAC,MAAM,KAAA,EAAE,KAAK,KAAA,EAAE,GAAG,KAAA,KAAK,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC;IAC/D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,6BAA6B,EAAE,CAAC;IAC/C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,+BAA+B,EAAE,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACnF,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC7C,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,IAAI,CAAC;IAChD,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,OAAO,CAAC;CAClD;AAED,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,eAAO,MAAM,uBAAuB,QAAyB,CAAC;AAE9D,eAAO,MAAM,oBAAoB,GAAI,MAAM,oBAAoB,KAAG,IAAI,IAAI,0BAEzE,CAAC;AAMF,QAAA,MAAM,oBAAoB,GAAI,MAAM,oBAAoB,KAAG,MAa1D,CAAC;AAEF,iBAAS,4BAA4B,CAAC,KAAK,EAAE,MAAM,qBA4SlD;AAED,OAAO,EAAE,4BAA4B,EAAE,oBAAoB,EAAE,CAAC"}
1
+ {"version":3,"file":"CaseContactsSelectorExternal.d.ts","sourceRoot":"","sources":["../../../src/Functional/CaseContactsSelectorExternal.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,6BAA6B,EAAE,MAAM,yCAAyC,CAAC;AACxF,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAE,MAAM,wCAAwC,CAAC;AAClE,OAAO,EAAuB,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAcxD,OAAO,KAA4D,MAAM,OAAO,CAAC;AAGjF,OAAO,EAAE,+BAA+B,EAAE,MAAM,wCAAwC,CAAC;AAEzF,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,0BAA0B,CAAC;AACzE,UAAU,MAAO,SAAQ,gBAAgB;IACrC,qBAAqB,EAAE,MAAM,CAAC;IAC9B,QAAQ,EAAE,CAAC,QAAQ,GAAG,0BAA0B,CAAC,EAAE,CAAC;IACpD,QAAQ,EAAE,CAAC,gBAAgB,EAAE,CAAC,QAAQ,GAAG,0BAA0B,CAAC,EAAE,KAAK,IAAI,CAAC;IAChF,kBAAkB,EAAE,QAAQ,CAAC;IAC7B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,QAAQ,EAAE,CAAC;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,CAAC,MAAM,KAAA,EAAE,KAAK,KAAA,EAAE,GAAG,KAAA,KAAK,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC;IAC/D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,6BAA6B,EAAE,CAAC;IAC/C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,+BAA+B,EAAE,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACnF,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC7C,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,IAAI,CAAC;IAChD,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,OAAO,CAAC;CAClD;AAED,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,eAAO,MAAM,uBAAuB,QAAyB,CAAC;AAE9D,eAAO,MAAM,oBAAoB,GAAI,MAAM,oBAAoB,KAAG,IAAI,IAAI,0BAEzE,CAAC;AAMF,QAAA,MAAM,oBAAoB,GAAI,MAAM,oBAAoB,KAAG,MAa1D,CAAC;AAEF,iBAAS,4BAA4B,CAAC,KAAK,EAAE,MAAM,qBAyUlD;AAED,OAAO,EAAE,4BAA4B,EAAE,oBAAoB,EAAE,CAAC"}
@@ -130,6 +130,11 @@ function CaseContactsSelectorExternal(props) {
130
130
  });
131
131
  // eslint-disable-next-line react-hooks/exhaustive-deps
132
132
  const debouncedSearch = useCallback(debounce(searchContacts, 400), [apolloClient]);
133
+ const fetchAllActiveContacts = () => {
134
+ currentSearchQuery.current = '';
135
+ debouncedSearch.cancel();
136
+ searchContacts('');
137
+ };
133
138
  useEffect(() => {
134
139
  const { selectedAccountNumber, loggedInUserRights, isExportingPDF } = props;
135
140
  const isNonOrgAdminSameAccount = loggedInUserRights.isExternal() && loggedInUserRights.isNonOrgAdminWithSameAccount(selectedAccountNumber);
@@ -223,32 +228,47 @@ function CaseContactsSelectorExternal(props) {
223
228
  })),
224
229
  ];
225
230
  }, [displayedContacts, getCustomEmails]);
231
+ const isOwnerSelectionComplete = (query, options) => {
232
+ if (isEmpty(query)) {
233
+ return true;
234
+ }
235
+ const selectedLabel = !isEmpty(selected) ? getHydraContactLabel(selected[0]) : '';
236
+ if (query === selectedLabel) {
237
+ return true;
238
+ }
239
+ return options.some((option) => getHydraContactLabel(option.contact) === query);
240
+ };
226
241
  /**
227
242
  * update the shown options in the dropdown and reset the amount of options show to the default.
228
243
  * @param query The user search term
229
244
  */
230
245
  const onQueryUpdated = (query) => {
231
246
  setHasNextPage(false);
232
- if (isEmpty(query)) {
233
- currentSearchQuery.current = '';
234
- debouncedSearch.cancel();
235
- searchContacts('');
247
+ if (isOwnerSelectionComplete(query, allOptions)) {
248
+ fetchAllActiveContacts();
249
+ setFilteredContacts(allOptions);
250
+ return;
251
+ }
252
+ const localFiltered = filter(allOptions, (option) => getHydraContactLabel(option.contact).toLowerCase().includes(query.toLowerCase()));
253
+ setFilteredContacts(localFiltered);
254
+ debouncedSearch(query);
255
+ };
256
+ const onDropdownOpenChange = (isOpen) => {
257
+ if (!isOpen) {
258
+ return;
259
+ }
260
+ if (currentSearchQuery.current) {
261
+ fetchAllActiveContacts();
236
262
  }
237
263
  else {
238
- const localFiltered = filter(allOptions, (option) => getHydraContactLabel(option.contact).toLowerCase().includes(query.toLowerCase()));
239
- setFilteredContacts(localFiltered);
240
- debouncedSearch(query);
264
+ setFilteredContacts(allOptions);
241
265
  }
242
266
  };
243
267
  /**
244
268
  * Reset the filtered contacts and the current offset.
245
269
  */
246
270
  const onClearQuery = () => {
247
- if (currentSearchQuery.current) {
248
- currentSearchQuery.current = '';
249
- debouncedSearch.cancel();
250
- searchContacts('');
251
- }
271
+ fetchAllActiveContacts();
252
272
  setFilteredContacts(allOptions);
253
273
  };
254
274
  /**
@@ -270,6 +290,6 @@ function CaseContactsSelectorExternal(props) {
270
290
  useEffect(() => {
271
291
  setFilteredContacts(allOptions);
272
292
  }, [displayedContacts, customEmails, allOptions, selected]);
273
- return (React.createElement(OwnerTypeaheadDropdown, { id: props.id || '', hasClearButton: true, multiple: !!props.multiple, contactSsoUsername: contactSsoUsername, groupContactsAndEmails: groupContactsAndEmails, selected: !isEmpty(props.selected) ? props.selected : [], options: filteredContacts, placeholder: props.placeholder || 'Search by name or username', "data-tracking-id": "external-case-contact-selector", isDisabled: isDisabled, status: isInvalid ? 'danger' : undefined, onChipRemoved: onChipRemoved, onClearQuery: onClearQuery, onQueryUpdated: onQueryUpdated, onSelect: onCustomerContactSelect, canAddNew: canAddNew, getCreateNewText: props === null || props === void 0 ? void 0 : props.getCreateNewText, isLoading: isLoading || isLoadingMore, onHandleLoadMore: loadMoreContacts, isShowMoreOptionVisible: hasNextPage }));
293
+ return (React.createElement(OwnerTypeaheadDropdown, { id: props.id || '', hasClearButton: true, multiple: !!props.multiple, contactSsoUsername: contactSsoUsername, groupContactsAndEmails: groupContactsAndEmails, selected: !isEmpty(props.selected) ? props.selected : [], options: filteredContacts, placeholder: props.placeholder || 'Search by name or username', "data-tracking-id": "external-case-contact-selector", isDisabled: isDisabled, status: isInvalid ? 'danger' : undefined, onChipRemoved: onChipRemoved, onClearQuery: onClearQuery, onOpenChange: onDropdownOpenChange, onQueryUpdated: onQueryUpdated, onSelect: onCustomerContactSelect, canAddNew: canAddNew, getCreateNewText: props === null || props === void 0 ? void 0 : props.getCreateNewText, isLoading: isLoading || isLoadingMore, onHandleLoadMore: loadMoreContacts, isShowMoreOptionVisible: hasNextPage }));
274
294
  }
275
295
  export { CaseContactsSelectorExternal, getHydraContactLabel };
@@ -1 +1 @@
1
- {"version":3,"file":"RichTextEditor.d.ts","sourceRoot":"","sources":["../../../src/RichTextEditor/RichTextEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA+B9B,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAmD,MAAM,OAAO,CAAC;AAMxE,MAAM,WAAW,oBAAoB;IACjC,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,oDAAoD;IACpD,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yBAAyB;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kCAAkC;IAClC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,mDAAmD;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gDAAgD;IAChD,mBAAmB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD,oBAAoB;IACpB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,oBAAoB;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,mBAAmB;IACnB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,gEAAgE;IAChE,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,KAAK,IAAI,CAAC;IACrC,uBAAuB;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACnD;AAQD,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,oBAAoB,CAAC,qBAgvBnE;AAED,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"RichTextEditor.d.ts","sourceRoot":"","sources":["../../../src/RichTextEditor/RichTextEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA+B9B,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAmD,MAAM,OAAO,CAAC;AAOxE,MAAM,WAAW,oBAAoB;IACjC,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,oDAAoD;IACpD,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yBAAyB;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kCAAkC;IAClC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,mDAAmD;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gDAAgD;IAChD,mBAAmB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD,oBAAoB;IACpB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,oBAAoB;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,mBAAmB;IACnB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,gEAAgE;IAChE,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,KAAK,IAAI,CAAC;IACrC,uBAAuB;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACnD;AAQD,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,oBAAoB,CAAC,qBAovBnE;AAED,eAAe,cAAc,CAAC"}
@@ -24,6 +24,7 @@ import StrikethroughIcon from '@patternfly/react-icons/dist/js/icons/strikethrou
24
24
  import React, { useCallback, useEffect, useRef, useState } from 'react';
25
25
  import { useTranslation } from 'react-i18next';
26
26
  import { ToastNotification } from '../ToastNotification';
27
+ import { isValidUrl, normalizeLinkUrl } from './linkUrl';
27
28
  import { useQuillEditor } from './useQuillEditor';
28
29
  const defaultCharLimit = 32000;
29
30
  const defaultMinCharsForCounter = 31800;
@@ -173,10 +174,10 @@ export function RichTextEditor(props) {
173
174
  const onLinkInsert = useCallback(() => {
174
175
  const quill = quillRef.current;
175
176
  const saved = savedSelectionRef.current;
176
- const url = linkUrl.trim();
177
- const text = linkText.trim() || url;
177
+ const url = normalizeLinkUrl(linkUrl);
178
178
  if (!quill || !saved || !url)
179
179
  return;
180
+ const text = linkText.trim() || url;
180
181
  if (saved.length > 0) {
181
182
  quill.setSelection(saved.index, saved.length, 'silent');
182
183
  quill.format('link', url);
@@ -446,7 +447,7 @@ export function RichTextEditor(props) {
446
447
  React.createElement(FormGroup, { label: "URL", isRequired: true, fieldId: "link-url-input" },
447
448
  React.createElement(TextInput, { id: "link-url-input", type: "url", value: linkUrl, onChange: onLinkUrlChange, placeholder: "https://", onKeyDown: onLinkUrlKeyDown }))),
448
449
  React.createElement(ModalFooter, null,
449
- React.createElement(Button, { variant: ButtonVariant.primary, onClick: onLinkInsert, isDisabled: !linkUrl.trim() }, "Insert"),
450
+ React.createElement(Button, { variant: ButtonVariant.primary, onClick: onLinkInsert, isDisabled: !linkUrl.trim() || !isValidUrl(linkUrl.trim()) }, "Insert"),
450
451
  React.createElement(Button, { variant: ButtonVariant.link, onClick: onLinkCancel }, "Cancel"))),
451
452
  React.createElement("input", { ref: fileInputRef, type: "file", accept: "image/jpeg,image/png,image/gif,image/svg+xml", style: { display: 'none' }, onChange: handleFileInputChange, tabIndex: -1 }),
452
453
  !onImageUpload && (React.createElement(Modal, { variant: ModalVariant.small, isOpen: isImageModalOpen, onClose: onImageCancel, "aria-labelledby": "insert-image-title" },
@@ -0,0 +1,18 @@
1
+ import type { default as Quill } from 'quill';
2
+ /**
3
+ * Registers auto-link behaviour on a Quill instance:
4
+ *
5
+ * 1. **Type** — after a delimiter character, checks the preceding token
6
+ * for URL / case-ID / BZ patterns.
7
+ * 2. **Paste** — when the insert is longer than one character, scans the
8
+ * full inserted range.
9
+ * 3. **Blur** — a full-document scan so trailing tokens without a
10
+ * delimiter still get linked before submit.
11
+ */
12
+ export declare function registerAutoLinkModule(quill: Quill): void;
13
+ /**
14
+ * Scans the entire document for unlinkified tokens.
15
+ * Call this on blur so trailing URLs / IDs without a delimiter are caught.
16
+ */
17
+ export declare function linkifyFullDocument(quill: Quill): void;
18
+ //# sourceMappingURL=autoLinkModule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autoLinkModule.d.ts","sourceRoot":"","sources":["../../../src/RichTextEditor/autoLinkModule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,IAAI,KAAK,EAA8B,MAAM,OAAO,CAAC;AAyE1E;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CA0EzD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAItD"}
@@ -0,0 +1,156 @@
1
+ function getCaseIdHref(caseId) {
2
+ const host = typeof window !== 'undefined' ? window.location.host : 'access.redhat.com';
3
+ return `https://${host}/support/cases/#/case/${caseId}`;
4
+ }
5
+ function getBugzillaHref(bugId) {
6
+ return `https://bugzilla.redhat.com/show_bug.cgi?id=${bugId}`;
7
+ }
8
+ const DELIMITER_RE = /[\s.,;:!?)}\]>]/;
9
+ const CASE_ID_RE = /(?:^|[\s(])(\d{8})$/;
10
+ const BZ_RE = /(?:^|[\s(])(?:BZ[\s-]*|Bug[#\s]?)(\d{7,})$/i;
11
+ const URL_RE = /(?:^|[\s(])(https?:\/\/[-A-Z0-9+&@#/%?=~_|!:,.;]*[-A-Z0-9+&@#/%=~_|])/gi;
12
+ const URL_AT_END_RE = /(https?:\/\/[-A-Z0-9+&@#/%?=~_|!:,.;]*[-A-Z0-9+&@#/%=~_|])$/i;
13
+ const WORD_CASE_ID_RE = /^(\d{8})$/;
14
+ const WORD_BZ_RE = /^(?:BZ[\s-]*|Bug[#\s]?)(\d{7,})$/i;
15
+ /**
16
+ * Scans a text range inside the Quill document and applies link formatting
17
+ * to all recognised URL, case-ID, and BZ patterns that aren't already linked.
18
+ *
19
+ * Used by the blur scan and paste scan to cover text that was not preceded
20
+ * by a delimiter character.
21
+ */
22
+ function linkifyRange(quill, rangeStart, rangeLength) {
23
+ const text = quill.getText(rangeStart, rangeLength);
24
+ URL_RE.lastIndex = 0;
25
+ let urlMatch;
26
+ while ((urlMatch = URL_RE.exec(text)) !== null) {
27
+ const url = urlMatch[1];
28
+ const matchOffset = urlMatch.index + urlMatch[0].length - url.length;
29
+ const absStart = rangeStart + matchOffset;
30
+ const format = quill.getFormat(absStart, url.length);
31
+ if (!format.link) {
32
+ quill.formatText(absStart, url.length, 'link', url, 'api');
33
+ }
34
+ }
35
+ const lines = text.split('\n');
36
+ let offset = 0;
37
+ for (const line of lines) {
38
+ const words = line.split(/(\s+)/);
39
+ let wordOffset = offset;
40
+ for (const word of words) {
41
+ if (/\S/.test(word)) {
42
+ const caseMatch = WORD_CASE_ID_RE.exec(word);
43
+ if (caseMatch) {
44
+ const absStart = rangeStart + wordOffset;
45
+ const fmt = quill.getFormat(absStart, 8);
46
+ if (!fmt.link) {
47
+ quill.formatText(absStart, 8, 'link', getCaseIdHref(caseMatch[1]), 'api');
48
+ }
49
+ }
50
+ else {
51
+ const bzMatch = WORD_BZ_RE.exec(word);
52
+ if (bzMatch) {
53
+ const absStart = rangeStart + wordOffset;
54
+ const fmt = quill.getFormat(absStart, word.length);
55
+ if (!fmt.link) {
56
+ quill.formatText(absStart, word.length, 'link', getBugzillaHref(bzMatch[1]), 'api');
57
+ }
58
+ }
59
+ }
60
+ }
61
+ wordOffset += word.length;
62
+ }
63
+ offset += line.length + 1;
64
+ }
65
+ }
66
+ /**
67
+ * Registers auto-link behaviour on a Quill instance:
68
+ *
69
+ * 1. **Type** — after a delimiter character, checks the preceding token
70
+ * for URL / case-ID / BZ patterns.
71
+ * 2. **Paste** — when the insert is longer than one character, scans the
72
+ * full inserted range.
73
+ * 3. **Blur** — a full-document scan so trailing tokens without a
74
+ * delimiter still get linked before submit.
75
+ */
76
+ export function registerAutoLinkModule(quill) {
77
+ quill.on('text-change', (delta, _oldDelta, source) => {
78
+ if (source !== 'user')
79
+ return;
80
+ const ops = delta.ops;
81
+ if (!ops || ops.length === 0)
82
+ return;
83
+ const lastOp = ops.at(-1);
84
+ if (!lastOp || typeof lastOp.insert !== 'string')
85
+ return;
86
+ const insertedText = lastOp.insert;
87
+ // Paste: multi-char insert — scan the full inserted range
88
+ if (insertedText.length > 1) {
89
+ let insertOffset = 0;
90
+ for (const op of ops) {
91
+ if (!op.retain)
92
+ break;
93
+ insertOffset += op.retain;
94
+ }
95
+ setTimeout(() => linkifyRange(quill, insertOffset, insertedText.length), 0);
96
+ return;
97
+ }
98
+ // Type: single-char delimiter
99
+ if (!DELIMITER_RE.test(insertedText))
100
+ return;
101
+ const selection = quill.getSelection();
102
+ if (!selection)
103
+ return;
104
+ const cursorIndex = selection.index;
105
+ const text = quill.getText(0, cursorIndex);
106
+ let lineStart = text.lastIndexOf('\n', cursorIndex - 2);
107
+ if (lineStart === -1)
108
+ lineStart = 0;
109
+ else
110
+ lineStart += 1;
111
+ const lineText = text.slice(lineStart, cursorIndex - 1);
112
+ // URL — try first so 8-digit IDs inside URLs are not separately linked
113
+ const urlMatch = URL_AT_END_RE.exec(lineText);
114
+ if (urlMatch) {
115
+ const url = urlMatch[1];
116
+ const matchStart = lineStart + lineText.length - url.length;
117
+ const format = quill.getFormat(matchStart, url.length);
118
+ if (!format.link) {
119
+ quill.formatText(matchStart, url.length, 'link', url, 'api');
120
+ }
121
+ return;
122
+ }
123
+ // Case ID (8-digit number)
124
+ const caseMatch = CASE_ID_RE.exec(lineText);
125
+ if (caseMatch) {
126
+ const caseId = caseMatch[1];
127
+ const matchStart = lineStart + lineText.length - caseId.length;
128
+ const format = quill.getFormat(matchStart, caseId.length);
129
+ if (!format.link) {
130
+ quill.formatText(matchStart, caseId.length, 'link', getCaseIdHref(caseId), 'api');
131
+ }
132
+ return;
133
+ }
134
+ // Bugzilla
135
+ const bzMatch = BZ_RE.exec(lineText);
136
+ if (bzMatch) {
137
+ const bugId = bzMatch[1];
138
+ const fullMatch = bzMatch[0].trimStart();
139
+ const matchStart = lineStart + lineText.length - fullMatch.length;
140
+ const format = quill.getFormat(matchStart, fullMatch.length);
141
+ if (!format.link) {
142
+ quill.formatText(matchStart, fullMatch.length, 'link', getBugzillaHref(bugId), 'api');
143
+ }
144
+ }
145
+ });
146
+ }
147
+ /**
148
+ * Scans the entire document for unlinkified tokens.
149
+ * Call this on blur so trailing URLs / IDs without a delimiter are caught.
150
+ */
151
+ export function linkifyFullDocument(quill) {
152
+ const length = quill.getLength();
153
+ if (length <= 1)
154
+ return;
155
+ linkifyRange(quill, 0, length - 1);
156
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Returns a normalized http(s)/mailto URL, or null if the value is not allowed.
3
+ * Protocol-less hosts (e.g. example.com) are treated as https.
4
+ */
5
+ export declare function normalizeLinkUrl(url: string): string | null;
6
+ export declare function isValidUrl(url: string): boolean;
7
+ //# sourceMappingURL=linkUrl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linkUrl.d.ts","sourceRoot":"","sources":["../../../src/RichTextEditor/linkUrl.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAa3D;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE/C"}
@@ -0,0 +1,26 @@
1
+ const ALLOWED_PROTOCOLS = new Set(['http:', 'https:', 'mailto:']);
2
+ /**
3
+ * Returns a normalized http(s)/mailto URL, or null if the value is not allowed.
4
+ * Protocol-less hosts (e.g. example.com) are treated as https.
5
+ */
6
+ export function normalizeLinkUrl(url) {
7
+ const trimmed = url.trim();
8
+ if (!trimmed)
9
+ return null;
10
+ try {
11
+ const hasAllowedPrefix = /^https?:\/\//i.test(trimmed) || /^mailto:/i.test(trimmed);
12
+ const candidate = hasAllowedPrefix ? trimmed : `https://${trimmed}`;
13
+ const parsed = new URL(candidate);
14
+ if (!ALLOWED_PROTOCOLS.has(parsed.protocol))
15
+ return null;
16
+ if (parsed.protocol !== 'mailto:' && !parsed.hostname.includes('.'))
17
+ return null;
18
+ return candidate;
19
+ }
20
+ catch (_a) {
21
+ return null;
22
+ }
23
+ }
24
+ export function isValidUrl(url) {
25
+ return normalizeLinkUrl(url) !== null;
26
+ }
@@ -13,7 +13,8 @@
13
13
 
14
14
  .richtext-editor-content {
15
15
  resize: vertical;
16
- overflow: auto;
16
+ overflow-y: auto;
17
+ overflow-x: hidden;
17
18
  font-size: 15px;
18
19
  min-width: 100%;
19
20
  outline: none;
@@ -33,6 +34,8 @@
33
34
  outline: none;
34
35
  border: none;
35
36
  min-height: 100px;
37
+ overflow-wrap: break-word;
38
+ word-break: break-word;
36
39
  }
37
40
 
38
41
  .richtext-editor-content .ql-editor:focus {
@@ -41,10 +44,12 @@
41
44
  box-shadow: none;
42
45
  }
43
46
 
44
- .richtext-editor-content:empty::before {
47
+ .richtext-editor-content .ql-editor.ql-blank::before {
45
48
  content: attr(data-placeholder);
46
49
  color: var(--pf-t--global--text--color--placeholder, #6a6e73);
47
50
  pointer-events: none;
51
+ position: absolute;
52
+ font-style: normal;
48
53
  }
49
54
 
50
55
  .richtext-editor-content ul,
@@ -56,10 +61,34 @@
56
61
  list-style-type: disc;
57
62
  }
58
63
 
64
+ .richtext-editor-content li[data-list='bullet'].ql-indent-1 {
65
+ list-style-type: circle;
66
+ }
67
+
68
+ .richtext-editor-content li[data-list='bullet'].ql-indent-2 {
69
+ list-style-type: square;
70
+ }
71
+
72
+ .richtext-editor-content li[data-list='bullet'].ql-indent-3 {
73
+ list-style-type: disc;
74
+ }
75
+
76
+ .richtext-editor-content li[data-list='bullet'].ql-indent-4 {
77
+ list-style-type: circle;
78
+ }
79
+
59
80
  .richtext-editor-content li[data-list='ordered'] {
60
81
  list-style-type: decimal;
61
82
  }
62
83
 
84
+ .richtext-editor-content li[data-list='ordered'].ql-indent-1 {
85
+ list-style-type: lower-alpha;
86
+ }
87
+
88
+ .richtext-editor-content li[data-list='ordered'].ql-indent-2 {
89
+ list-style-type: lower-roman;
90
+ }
91
+
63
92
  .richtext-editor-content .ql-align-center {
64
93
  text-align: center;
65
94
  }
@@ -73,23 +102,23 @@
73
102
  }
74
103
 
75
104
  .richtext-editor-content .ql-indent-1 {
76
- padding-left: 3em;
105
+ margin-left: 3em;
77
106
  }
78
107
 
79
108
  .richtext-editor-content .ql-indent-2 {
80
- padding-left: 6em;
109
+ margin-left: 6em;
81
110
  }
82
111
 
83
112
  .richtext-editor-content .ql-indent-3 {
84
- padding-left: 9em;
113
+ margin-left: 9em;
85
114
  }
86
115
 
87
116
  .richtext-editor-content .ql-indent-4 {
88
- padding-left: 12em;
117
+ margin-left: 12em;
89
118
  }
90
119
 
91
120
  .richtext-editor-content .ql-indent-5 {
92
- padding-left: 15em;
121
+ margin-left: 15em;
93
122
  }
94
123
 
95
124
  .richtext-editor-content a {
@@ -1 +1 @@
1
- {"version":3,"file":"useQuillEditor.d.ts","sourceRoot":"","sources":["../../../src/RichTextEditor/useQuillEditor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,MAAM,WAAW,qBAAqB;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAC9D,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AAID,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB;;;;;wBAwDlB,MAAM;4BAgBF,OAAO;;EASrD"}
1
+ {"version":3,"file":"useQuillEditor.d.ts","sourceRoot":"","sources":["../../../src/RichTextEditor/useQuillEditor.ts"],"names":[],"mappings":"AACA,OAAO,KAAmC,MAAM,OAAO,CAAC;AAKxD,MAAM,WAAW,qBAAqB;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAC9D,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AAoFD,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB;;;;;wBA+ElB,MAAM;4BAgBF,OAAO;;EASrD"}
@@ -1,7 +1,84 @@
1
1
  import DOMPurify from 'dompurify';
2
2
  import Quill from 'quill';
3
3
  import { useCallback, useEffect, useRef, useState } from 'react';
4
+ import { linkifyFullDocument, registerAutoLinkModule } from './autoLinkModule';
4
5
  const ALLOWED_FORMATS = ['header', 'bold', 'italic', 'underline', 'strike', 'list', 'indent', 'align', 'link', 'image'];
6
+ /**
7
+ * Quill 2 internally represents ALL lists as <ol> and distinguishes bullet vs.
8
+ * ordered via data-list attributes on <li>. root.innerHTML preserves text-entity
9
+ * encoding (important for literal angle-brackets), but outputs this internal
10
+ * format. This helper converts it to semantic <ul>/<ol> and nests indented
11
+ * items before sanitization.
12
+ */
13
+ function normalizeQuillListHtml(rawHtml) {
14
+ const tmp = document.createElement('div');
15
+ tmp.innerHTML = rawHtml;
16
+ tmp.querySelectorAll('.ql-ui').forEach((el) => el.remove());
17
+ tmp.querySelectorAll('ol').forEach((ol) => {
18
+ const items = Array.from(ol.querySelectorAll(':scope > li'));
19
+ const allBullet = items.length > 0 && items.every((li) => li instanceof HTMLElement && li.dataset.list === 'bullet');
20
+ if (allBullet) {
21
+ const ul = document.createElement('ul');
22
+ for (const attr of Array.from(ol.attributes)) {
23
+ ul.setAttribute(attr.name, attr.value);
24
+ }
25
+ while (ol.firstChild)
26
+ ul.appendChild(ol.firstChild);
27
+ ol.replaceWith(ul);
28
+ }
29
+ items.forEach((li) => {
30
+ if (li instanceof HTMLElement) {
31
+ delete li.dataset.list;
32
+ }
33
+ });
34
+ });
35
+ tmp.querySelectorAll('ul, ol').forEach((list) => {
36
+ if (list instanceof HTMLElement)
37
+ nestIndentedItems(list);
38
+ });
39
+ return tmp.innerHTML;
40
+ }
41
+ function tryNestIndentedItem(item, list) {
42
+ var _a;
43
+ const indentMatch = /ql-indent-(\d+)/.exec((_a = item.className) !== null && _a !== void 0 ? _a : '');
44
+ if (!indentMatch)
45
+ return false;
46
+ const level = Number.parseInt(indentMatch[1], 10);
47
+ item.classList.remove(`ql-indent-${level}`);
48
+ if (!item.className.trim())
49
+ item.removeAttribute('class');
50
+ let prev = item.previousElementSibling;
51
+ while (prev && prev.tagName !== 'LI')
52
+ prev = prev.previousElementSibling;
53
+ if (!prev)
54
+ return false;
55
+ let nested = prev.querySelector(':scope > ul, :scope > ol');
56
+ if (!(nested instanceof HTMLElement)) {
57
+ nested = document.createElement(list.tagName.toLowerCase());
58
+ prev.appendChild(nested);
59
+ }
60
+ nested.appendChild(item);
61
+ if (level > 1)
62
+ item.classList.add(`ql-indent-${level - 1}`);
63
+ return true;
64
+ }
65
+ function nestIndentedItems(list) {
66
+ let changed = true;
67
+ while (changed) {
68
+ changed = false;
69
+ const items = Array.from(list.querySelectorAll(':scope > li'));
70
+ for (const item of items) {
71
+ if (tryNestIndentedItem(item, list))
72
+ changed = true;
73
+ }
74
+ }
75
+ // Newly created nested lists are not in the original querySelectorAll snapshot.
76
+ // Recurse so indent 2+ items become true nested lists instead of leftover ql-indent-* siblings.
77
+ for (const nested of Array.from(list.querySelectorAll(':scope > li > ul, :scope > li > ol'))) {
78
+ if (nested instanceof HTMLElement)
79
+ nestIndentedItems(nested);
80
+ }
81
+ }
5
82
  export function useQuillEditor(options) {
6
83
  const editorRef = useRef(null);
7
84
  const quillRef = useRef(null);
@@ -14,19 +91,40 @@ export function useQuillEditor(options) {
14
91
  if (!editorRef.current || quillRef.current)
15
92
  return;
16
93
  const quill = new Quill(editorRef.current, {
17
- theme: false,
18
94
  modules: {
19
95
  toolbar: false,
96
+ keyboard: {
97
+ bindings: {
98
+ indent: {
99
+ key: 'Tab',
100
+ format: ['list'],
101
+ handler(_range, _context) {
102
+ this.quill.format('indent', '+1', 'user');
103
+ return false;
104
+ },
105
+ },
106
+ outdent: {
107
+ key: 'Tab',
108
+ shiftKey: true,
109
+ format: ['list'],
110
+ handler(_range, _context) {
111
+ this.quill.format('indent', '-1', 'user');
112
+ return false;
113
+ },
114
+ },
115
+ },
116
+ },
20
117
  clipboard: { matchVisual: false },
21
118
  },
22
119
  formats: ALLOWED_FORMATS,
23
120
  placeholder: optionsRef.current.placeholder,
24
121
  readOnly: optionsRef.current.readOnly,
25
122
  });
123
+ registerAutoLinkModule(quill);
26
124
  quill.on('text-change', () => {
27
125
  var _a, _b, _c, _d;
28
126
  isInternalChange.current = true;
29
- const html = DOMPurify.sanitize(quill.getSemanticHTML());
127
+ const html = DOMPurify.sanitize(normalizeQuillListHtml(quill.root.innerHTML));
30
128
  (_b = (_a = optionsRef.current).onChange) === null || _b === void 0 ? void 0 : _b.call(_a, html);
31
129
  (_d = (_c = optionsRef.current).onChangePlainText) === null || _d === void 0 ? void 0 : _d.call(_c, quill.getText().trim());
32
130
  const sel = quill.getSelection();
@@ -45,6 +143,7 @@ export function useQuillEditor(options) {
45
143
  }
46
144
  }
47
145
  else {
146
+ linkifyFullDocument(quill);
48
147
  (_f = (_e = optionsRef.current).onBlur) === null || _f === void 0 ? void 0 : _f.call(_e);
49
148
  }
50
149
  });
@@ -58,7 +157,7 @@ export function useQuillEditor(options) {
58
157
  const quill = quillRef.current;
59
158
  if (!quill || isInternalChange.current)
60
159
  return;
61
- const currentHtml = DOMPurify.sanitize(quill.getSemanticHTML());
160
+ const currentHtml = DOMPurify.sanitize(normalizeQuillListHtml(quill.root.innerHTML));
62
161
  if (currentHtml === html)
63
162
  return;
64
163
  const selection = quill.getSelection();
@@ -61,8 +61,6 @@ export function SingleSelectDropdown(props) {
61
61
  return (React.createElement(Select, Object.assign({ shouldFocusToggleOnSelect: true, shouldPreventScrollOnItemFocus: true, id: id || '', className: className || '', "data-tracking-id": dataTrackingId, isOpen: isOpen, selected: selected, onSelect: onLocalSelect, onOpenChange: (isOpen) => setIsOpen(isOpen), toggle: toggle, popperProps: { direction: 'down', enableFlip: false }, isScrollable: isScrollable, onBlur: onLocalBlur, "aria-label": ariaLabel, zIndex: zIndex, "aria-labelledby": ariaLabelledBy, maxMenuHeight: "20rem" }, restProps),
62
62
  React.createElement(SelectList, null, options.map((option, index) => {
63
63
  var _a;
64
- return (React.createElement(SelectOption, { key: option.value, value: option, description: option.description || '',
65
- // Fix the comparison to compare the entire objects or at least their values
66
- isSelected: isEqual(selected === null || selected === void 0 ? void 0 : selected.value, option.value), isDisabled: option.disabled, isFocused: focusedItemIndex === index, title: (option === null || option === void 0 ? void 0 : option.title) || option.label }, (_a = option === null || option === void 0 ? void 0 : option.element) !== null && _a !== void 0 ? _a : option.label));
64
+ return (React.createElement(SelectOption, { key: option.value, value: option, description: option.description || '', isSelected: isEqual(selected === null || selected === void 0 ? void 0 : selected.value, option.value), isDisabled: option.disabled, isFocused: focusedItemIndex === index, title: (option === null || option === void 0 ? void 0 : option.title) || option.label, "aria-label": option.label }, (_a = option === null || option === void 0 ? void 0 : option.element) !== null && _a !== void 0 ? _a : option.label));
67
65
  }))));
68
66
  }
@@ -9,8 +9,12 @@ export declare enum ToastType {
9
9
  SUCCESS = "success",
10
10
  WARNING = "warning"
11
11
  }
12
+ export type ToastActionLinks = React.ReactNode | ((closeToast?: () => void) => React.ReactNode);
13
+ export type ToastNotificationOptions = ToastOptions & {
14
+ actionLinks?: ToastActionLinks;
15
+ };
12
16
  declare class ToastNotification {
13
- static addDangerMessage(message: React.ReactNode, alertBody?: React.ReactNode, options?: ToastOptions): import("react-toastify").ToastId;
17
+ static addDangerMessage(message: React.ReactNode, alertBody?: React.ReactNode, options?: ToastNotificationOptions): import("react-toastify").ToastId;
14
18
  static addSuccessMessage(message: React.ReactNode, alertBody?: React.ReactNode, options?: ToastOptions): import("react-toastify").ToastId;
15
19
  static addWarningMessage(message: any, alertBody?: React.ReactNode | string, options?: ToastOptions): import("react-toastify").ToastId;
16
20
  static addInfoMessage(message: any, options?: ToastOptions): import("react-toastify").ToastId;
@@ -1 +1 @@
1
- {"version":3,"file":"ToastNotification.d.ts","sourceRoot":"","sources":["../../../src/ToastNotification/ToastNotification.tsx"],"names":[],"mappings":"AAAA,OAAO,2CAA2C,CAAC;AACnD,OAAO,yBAAyB,CAAC;AAIjC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAS,cAAc,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAErE,OAAO,EAA+C,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAEzG,oBAAY,SAAS;IACjB,MAAM,WAAW;IACjB,IAAI,SAAS;IACb,OAAO,YAAY;IACnB,OAAO,YAAY;CACtB;AAsBD,cAAM,iBAAiB;WACL,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,EAAE,OAAO,GAAE,YAAiB;WAalG,iBAAiB,CAC3B,OAAO,EAAE,KAAK,CAAC,SAAS,EACxB,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,EAC3B,OAAO,GAAE,YAAkC;WAajC,iBAAiB,CAAC,OAAO,KAAA,EAAE,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,MAAM,EAAE,OAAO,GAAE,YAAiB;WAY3F,cAAc,CAAC,OAAO,KAAA,EAAE,OAAO,GAAE,YAAkC;WAQnE,kBAAkB,CAAC,KAAK,KAAA,EAAE,eAAe,CAAC,EAAE,SAAS;WAOrD,QAAQ;WAIR,gBAAgB,CAAC,cAAc,EAAE,MAAM;WAIvC,UAAU,CAAC,OAAO,KAAA;IAIhC;;;OAGG;WACW,kBAAkB,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,KAAK,CAAC,SAAS,EAAE;CAG1F;AAED,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,CAAC"}
1
+ {"version":3,"file":"ToastNotification.d.ts","sourceRoot":"","sources":["../../../src/ToastNotification/ToastNotification.tsx"],"names":[],"mappings":"AAAA,OAAO,2CAA2C,CAAC;AACnD,OAAO,yBAAyB,CAAC;AAIjC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAS,cAAc,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAErE,OAAO,EAA+C,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAEzG,oBAAY,SAAS;IACjB,MAAM,WAAW;IACjB,IAAI,SAAS;IACb,OAAO,YAAY;IACnB,OAAO,YAAY;CACtB;AAED,MAAM,MAAM,gBAAgB,GAAG,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,UAAU,CAAC,EAAE,MAAM,IAAI,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;AAEhG,MAAM,MAAM,wBAAwB,GAAG,YAAY,GAAG;IAClD,WAAW,CAAC,EAAE,gBAAgB,CAAC;CAClC,CAAC;AA8BF,cAAM,iBAAiB;WACL,gBAAgB,CAC1B,OAAO,EAAE,KAAK,CAAC,SAAS,EACxB,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,EAC3B,OAAO,GAAE,wBAA6B;WAoB5B,iBAAiB,CAC3B,OAAO,EAAE,KAAK,CAAC,SAAS,EACxB,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,EAC3B,OAAO,GAAE,YAAkC;WAajC,iBAAiB,CAAC,OAAO,KAAA,EAAE,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,MAAM,EAAE,OAAO,GAAE,YAAiB;WAY3F,cAAc,CAAC,OAAO,KAAA,EAAE,OAAO,GAAE,YAAkC;WAQnE,kBAAkB,CAAC,KAAK,KAAA,EAAE,eAAe,CAAC,EAAE,SAAS;WAOrD,QAAQ;WAIR,gBAAgB,CAAC,cAAc,EAAE,MAAM;WAIvC,UAAU,CAAC,OAAO,KAAA;IAIhC;;;OAGG;WACW,kBAAkB,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,KAAK,CAAC,SAAS,EAAE;CAG1F;AAED,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,CAAC"}
@@ -1,3 +1,14 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
1
12
  import 'react-toastify/dist/ReactToastify.min.css';
2
13
  import './toastNotification.css';
3
14
  import { Alert, AlertActionCloseButton } from '@patternfly/react-core';
@@ -12,16 +23,18 @@ export var ToastType;
12
23
  ToastType["SUCCESS"] = "success";
13
24
  ToastType["WARNING"] = "warning";
14
25
  })(ToastType || (ToastType = {}));
15
- const AlertComponent = ({ variant, message, alertActions = {}, closeToast, children }) => {
16
- return (React.createElement(Alert, { variant: variant, title: message, actionClose: React.createElement(AlertActionCloseButton, { onClose: closeToast }) }, children));
26
+ const AlertComponent = ({ variant, message, alertActions, closeToast, children }) => {
27
+ return (React.createElement(Alert, { variant: variant, title: message, actionClose: React.createElement(AlertActionCloseButton, { onClose: closeToast }), actionLinks: alertActions }, children));
17
28
  };
29
+ const resolveActionLinks = (actionLinks, closeToast) => typeof actionLinks === 'function' ? actionLinks(closeToast) : actionLinks;
18
30
  const defaultCloseMs = 8000;
19
31
  const defaultToastOptions = { autoClose: defaultCloseMs };
20
32
  class ToastNotification {
21
33
  static addDangerMessage(message, alertBody, options = {}) {
34
+ const { actionLinks } = options, toastOptions = __rest(options, ["actionLinks"]);
22
35
  return toast(
23
36
  //@ts-ignore
24
- ({ closeToast }) => (React.createElement(AlertComponent, { variant: ToastType.DANGER, message: message, closeToast: closeToast }, alertBody)), Object.assign({}, options));
37
+ ({ closeToast }) => (React.createElement(AlertComponent, { variant: ToastType.DANGER, message: message, closeToast: closeToast, alertActions: resolveActionLinks(actionLinks, closeToast) }, alertBody)), Object.assign({}, toastOptions));
25
38
  }
26
39
  // removed options: ToastOptions = defaultToastOptions to fix auto-dismissing of success alerts.
27
40
  static addSuccessMessage(message, alertBody, options = defaultToastOptions) {
@@ -14,6 +14,8 @@
14
14
 
15
15
  .Toastify__toast {
16
16
  background: none;
17
+ /* react-toastify sets sans-serif; match alert title (h4 → Red Hat Display) */
18
+ font-family: var(--pf-t--global--font--family--heading, inherit);
17
19
  }
18
20
 
19
21
  .Toastify__toast-body {
@@ -24,3 +26,8 @@
24
26
  height: 100%;
25
27
  min-width: 500px;
26
28
  }
29
+
30
+ .Toastify__toast-body > .pf-v6-c-alert .pf-v6-c-alert__description,
31
+ .Toastify__toast-body > .pf-v6-c-alert .pf-v6-c-alert__action-group {
32
+ font-family: inherit;
33
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rh-support/components",
3
- "version": "2.5.226",
3
+ "version": "2.5.230",
4
4
  "description": "Contains all reusabel components for support app",
5
5
  "author": "Vikas Rathee <vrathee@redhat.com>",
6
6
  "license": "ISC",
@@ -62,8 +62,8 @@
62
62
  "@patternfly/react-icons": "6.2.1",
63
63
  "@patternfly/react-table": "6.2.1",
64
64
  "@rh-support/types": "2.0.26",
65
- "@rh-support/user-permissions": "2.5.174",
66
- "@rh-support/utils": "2.5.153",
65
+ "@rh-support/user-permissions": "2.5.178",
66
+ "@rh-support/utils": "2.5.157",
67
67
  "dompurify": "^2.2.6",
68
68
  "js-worker-search": "^1.4.1",
69
69
  "lazysizes": "^5.3.2",
@@ -103,5 +103,5 @@
103
103
  "defaults and supports es6-module",
104
104
  "maintained node versions"
105
105
  ],
106
- "gitHead": "7e9d99cabdfd48a98e1fda53bd6747c1797c548c"
106
+ "gitHead": "78790ed8b35b8acbc7a3a4a182d212499a0cd181"
107
107
  }