Implement replaceAll utility function

add #8331 release notes
This commit is contained in:
thatmattlove 2022-02-07 13:49:27 -07:00
parent 60f48326e1
commit 6c1507c88c
7 changed files with 63 additions and 13 deletions

View File

@ -6,6 +6,7 @@
* [#8548](https://github.com/netbox-community/netbox/issues/8548) - Fix display of VC members when position is zero
* [#8561](https://github.com/netbox-community/netbox/issues/8561) - Include option to connect a rear port to a console port
* [#8331](https://github.com/netbox-community/netbox/issues/8331) - Implement `replaceAll` string utility function to improve browser compatibility
---

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -8,11 +8,12 @@ import { DynamicParamsMap } from './dynamicParams';
import { isStaticParams, isOption } from './types';
import {
hasMore,
isTruthy,
hasError,
getElement,
isTruthy,
getApiData,
getElement,
isApiError,
replaceAll,
createElement,
uniqueByProperty,
findFirstAdjacent,
@ -461,7 +462,7 @@ export class APISelect {
// Set any primitive k/v pairs as data attributes on each option.
for (const [k, v] of Object.entries(result)) {
if (!['id', 'slug'].includes(k) && ['string', 'number', 'boolean'].includes(typeof v)) {
const key = k.replace(/_/g, '-');
const key = replaceAll(k, '_', '-');
data[key] = String(v);
}
// Set option to disabled if the result contains a matching key and is truthy.
@ -659,7 +660,7 @@ export class APISelect {
for (const [key, value] of this.pathValues.entries()) {
for (const result of this.url.matchAll(new RegExp(`({{${key}}})`, 'g'))) {
if (isTruthy(value)) {
url = url.replace(result[1], value.toString());
url = replaceAll(url, result[1], value.toString());
}
}
}
@ -741,7 +742,7 @@ export class APISelect {
* @param id DOM ID of the other element.
*/
private updatePathValues(id: string): void {
const key = id.replace(/^id_/gi, '');
const key = replaceAll(id, /^id_/i, '');
const element = getElement<HTMLSelectElement>(`id_${key}`);
if (element !== null) {
// If this element's URL contains Django template tags ({{), replace the template tag
@ -919,16 +920,18 @@ export class APISelect {
style.setAttribute('data-netbox', id);
// Scope the CSS to apply both the list item and the selected item.
style.innerHTML = `
style.innerHTML = replaceAll(
`
div.ss-values div.ss-value[data-id="${id}"],
div.ss-list div.ss-option:not(.ss-disabled)[data-id="${id}"]
{
background-color: ${bg} !important;
color: ${fg} !important;
}
`
.replace(/\n/g, '')
.trim();
`,
'\n',
'',
).trim();
// Add the style element to the DOM.
document.head.appendChild(style);

View File

@ -1,4 +1,4 @@
import { getElements, findFirstAdjacent } from '../util';
import { getElements, replaceAll, findFirstAdjacent } from '../util';
type InterfaceState = 'enabled' | 'disabled';
type ShowHide = 'show' | 'hide';
@ -105,9 +105,9 @@ class ButtonState {
*/
private toggleButton(): void {
if (this.buttonState === 'show') {
this.button.innerText = this.button.innerText.replace(/Show/g, 'Hide');
this.button.innerText = replaceAll(this.button.innerText, 'Show', 'Hide');
} else if (this.buttonState === 'hide') {
this.button.innerText = this.button.innerText.replace(/Hide/g, 'Show');
this.button.innerText = replaceAll(this.button.innerHTML, 'Hide', 'Show');
}
}

View File

@ -315,7 +315,7 @@ export function* getRowValues(table: HTMLTableRowElement): Generator<string> {
for (const element of table.querySelectorAll<HTMLTableCellElement>('td')) {
if (element !== null) {
if (isTruthy(element.innerText) && element.innerText !== '—') {
yield element.innerText.replace(/[\n\r]/g, '').trim();
yield replaceAll(element.innerText, '[\n\r]', '').trim();
}
}
}
@ -436,3 +436,49 @@ export function uniqueByProperty<T extends unknown, P extends keyof T>(arr: T[],
}
return Array.from(baseMap.values());
}
/**
* Replace all occurrences of a pattern with a replacement string.
*
* This is a browser-compatibility-focused drop-in replacement for `String.prototype.replaceAll()`,
* introduced in ES2021.
*
* @param input string to be processed.
* @param pattern regex pattern string or RegExp object to search for.
* @param replacement replacement substring with which `pattern` matches will be replaced.
* @returns processed version of `input`.
*/
export function replaceAll(input: string, pattern: string | RegExp, replacement: string): string {
// Ensure input is a string.
if (typeof input !== 'string') {
throw new TypeError("replaceAll 'input' argument must be a string");
}
// Ensure pattern is a string or RegExp.
if (typeof pattern !== 'string' && !(pattern instanceof RegExp)) {
throw new TypeError("replaceAll 'pattern' argument must be a string or RegExp instance");
}
// Ensure replacement is able to be stringified.
switch (typeof replacement) {
case 'boolean':
replacement = String(replacement);
break;
case 'number':
replacement = String(replacement);
break;
case 'string':
break;
default:
throw new TypeError("replaceAll 'replacement' argument must be stringifyable");
}
if (pattern instanceof RegExp) {
// Add global flag to existing RegExp object and deduplicate
const flags = Array.from(new Set([...pattern.flags.split(''), 'g'])).join('');
pattern = new RegExp(pattern.source, flags);
} else {
// Create a RegExp object with the global flag set.
pattern = new RegExp(pattern, 'g');
}
return input.replace(pattern, replacement);
}