uikit-react-public 0.49.2 → 0.49.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/common/formatConfidentialValue.d.ts +4 -1
- package/dist/index.js +456 -451
- package/lib/components/ConfidentialDisplayField/ConfidentialDisplayField.tsx +5 -1
- package/lib/components/ConfidentialDisplayField/Documentation.mdx +1 -1
- package/lib/components/ConfidentialDisplayField/__tests__/ConfidentialDisplayField.test.tsx +6 -6
- package/lib/components/ConfidentialInput/ConfidentialInput.tsx +2 -1
- package/lib/components/ConfidentialInput/Documentation.mdx +1 -1
- package/lib/components/ConfidentialInput/__tests__/ConfidentialInput.test.tsx +12 -12
- package/lib/components/common/formatConfidentialValue.ts +17 -5
- package/package.json +1 -1
|
@@ -24,6 +24,7 @@ const ConfidentialDisplayField = forwardRef<Ref, ConfidentialDisplayFieldProps>(
|
|
|
24
24
|
value,
|
|
25
25
|
numeric,
|
|
26
26
|
maskFormat,
|
|
27
|
+
format,
|
|
27
28
|
showable = false,
|
|
28
29
|
testId = NAME,
|
|
29
30
|
className,
|
|
@@ -45,7 +46,9 @@ const ConfidentialDisplayField = forwardRef<Ref, ConfidentialDisplayFieldProps>(
|
|
|
45
46
|
const valueIsShown = showable && isShown;
|
|
46
47
|
const displayedValue = valueIsShown
|
|
47
48
|
? stringValue
|
|
48
|
-
: formatConfidentialValue(stringValue, maskFormat
|
|
49
|
+
: formatConfidentialValue(stringValue, maskFormat, {
|
|
50
|
+
includeTemplateLiterals: !format,
|
|
51
|
+
});
|
|
49
52
|
|
|
50
53
|
useEffect(() => {
|
|
51
54
|
if (!showable) setIsShown(false);
|
|
@@ -68,6 +71,7 @@ const ConfidentialDisplayField = forwardRef<Ref, ConfidentialDisplayFieldProps>(
|
|
|
68
71
|
{...props}
|
|
69
72
|
ref={ref}
|
|
70
73
|
value={displayedValue}
|
|
74
|
+
format={format}
|
|
71
75
|
numeric={numeric ?? typeof value === 'number'}
|
|
72
76
|
testId={testId}
|
|
73
77
|
noMargins
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
|
|
15
15
|
`ConfidentialDisplayField` extends `DisplayField` with the same simple mask
|
|
16
16
|
format used by `ConfidentialInput`. `#` reveals a value character, `*` masks it
|
|
17
|
-
with a
|
|
17
|
+
with a `●`, and other characters are displayed as separators.
|
|
18
18
|
|
|
19
19
|
<Source
|
|
20
20
|
code={`<ConfidentialDisplayField
|
|
@@ -23,7 +23,7 @@ describe('ConfidentialDisplayField', () => {
|
|
|
23
23
|
|
|
24
24
|
expect(
|
|
25
25
|
screen.getByTestId('ucl-uikit-confidential-display-field')
|
|
26
|
-
).toHaveTextContent('
|
|
26
|
+
).toHaveTextContent('●●●●●●78');
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
test('applies a function mask', () => {
|
|
@@ -34,7 +34,7 @@ describe('ConfidentialDisplayField', () => {
|
|
|
34
34
|
|
|
35
35
|
expect(
|
|
36
36
|
screen.getByTestId('ucl-uikit-confidential-display-field')
|
|
37
|
-
).toHaveTextContent('
|
|
37
|
+
).toHaveTextContent('●●●● ending in 78');
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
test('does not add a visibility control by default', () => {
|
|
@@ -69,20 +69,20 @@ describe('ConfidentialDisplayField', () => {
|
|
|
69
69
|
await user.click(
|
|
70
70
|
screen.getByRole('button', { name: 'Hide confidential value' })
|
|
71
71
|
);
|
|
72
|
-
expect(output).toHaveTextContent('
|
|
72
|
+
expect(output).toHaveTextContent('●●●●●●78');
|
|
73
73
|
});
|
|
74
74
|
|
|
75
75
|
test('formats both masked and visible values', async () => {
|
|
76
76
|
const user = userEvent.setup();
|
|
77
77
|
renderDisplayField({
|
|
78
78
|
value: '123472',
|
|
79
|
-
maskFormat: '
|
|
79
|
+
maskFormat: '**-**-##',
|
|
80
80
|
format: '##-##-##',
|
|
81
81
|
showable: true,
|
|
82
82
|
});
|
|
83
83
|
|
|
84
84
|
const output = screen.getByTestId('ucl-uikit-confidential-display-field');
|
|
85
|
-
expect(output).toHaveTextContent('
|
|
85
|
+
expect(output).toHaveTextContent('●●-●●-72');
|
|
86
86
|
|
|
87
87
|
await user.click(
|
|
88
88
|
screen.getByRole('button', { name: 'Show confidential value' })
|
|
@@ -139,6 +139,6 @@ describe('ConfidentialDisplayField', () => {
|
|
|
139
139
|
|
|
140
140
|
expect(
|
|
141
141
|
screen.getByTestId('ucl-uikit-confidential-display-field')
|
|
142
|
-
).toHaveTextContent('
|
|
142
|
+
).toHaveTextContent('●●●●●●78');
|
|
143
143
|
});
|
|
144
144
|
});
|
|
@@ -125,7 +125,8 @@ const ConfidentialInput = forwardRef<Ref, ConfidentialInputProps>(
|
|
|
125
125
|
value === undefined ? uncontrolledValue : getStringValue(value);
|
|
126
126
|
const displayedMaskedValue = formatConfidentialValue(
|
|
127
127
|
stringValue,
|
|
128
|
-
maskFormat
|
|
128
|
+
maskFormat,
|
|
129
|
+
{ includeTemplateLiterals: !format }
|
|
129
130
|
);
|
|
130
131
|
const configuredValueIsVisible =
|
|
131
132
|
!isFocused &&
|
|
@@ -20,7 +20,7 @@ change events, and form submission while displaying a masked value.
|
|
|
20
20
|
## Usage
|
|
21
21
|
|
|
22
22
|
The string mask is a simple template. `#` reveals the corresponding value
|
|
23
|
-
character, `*` masks it with a
|
|
23
|
+
character, `*` masks it with a `●`, and all other characters are displayed as
|
|
24
24
|
separators. Asterisks returned by a formatter function are also displayed as
|
|
25
25
|
bullets. Focusing the input temporarily reveals the original value for editing,
|
|
26
26
|
and blurring restores the configured mask. When `showable` is `true`, the Eye
|
|
@@ -27,7 +27,7 @@ describe('ConfidentialInput', () => {
|
|
|
27
27
|
);
|
|
28
28
|
expect(
|
|
29
29
|
screen.getByTestId('ucl-uikit-confidential-input-mask')
|
|
30
|
-
).toHaveTextContent('1234
|
|
30
|
+
).toHaveTextContent('1234●●●●');
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
test('supports separators and masks characters beyond the template', () => {
|
|
@@ -39,7 +39,7 @@ describe('ConfidentialInput', () => {
|
|
|
39
39
|
|
|
40
40
|
expect(
|
|
41
41
|
screen.getByTestId('ucl-uikit-confidential-input-mask')
|
|
42
|
-
).toHaveTextContent('
|
|
42
|
+
).toHaveTextContent('●●-●●-72●');
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
test('applies a function mask', () => {
|
|
@@ -51,7 +51,7 @@ describe('ConfidentialInput', () => {
|
|
|
51
51
|
|
|
52
52
|
expect(
|
|
53
53
|
screen.getByTestId('ucl-uikit-confidential-input-mask')
|
|
54
|
-
).toHaveTextContent('
|
|
54
|
+
).toHaveTextContent('●●●● ending in 78');
|
|
55
55
|
});
|
|
56
56
|
|
|
57
57
|
test('does not add a visibility control by default', () => {
|
|
@@ -120,7 +120,7 @@ describe('ConfidentialInput', () => {
|
|
|
120
120
|
|
|
121
121
|
expect(
|
|
122
122
|
screen.getByTestId('ucl-uikit-confidential-input-mask')
|
|
123
|
-
).toHaveTextContent('
|
|
123
|
+
).toHaveTextContent('●●●●');
|
|
124
124
|
expect(onChange).toHaveBeenLastCalledWith(expect.objectContaining({}));
|
|
125
125
|
expect(onChange.mock.lastCall?.[0].target.value).toBe('1234');
|
|
126
126
|
});
|
|
@@ -189,7 +189,7 @@ describe('ConfidentialInput', () => {
|
|
|
189
189
|
) as HTMLInputElement;
|
|
190
190
|
expect(
|
|
191
191
|
screen.getByTestId('ucl-uikit-confidential-input-mask')
|
|
192
|
-
).toHaveTextContent('
|
|
192
|
+
).toHaveTextContent('●●-●●-72');
|
|
193
193
|
|
|
194
194
|
await user.click(input);
|
|
195
195
|
|
|
@@ -203,7 +203,7 @@ describe('ConfidentialInput', () => {
|
|
|
203
203
|
|
|
204
204
|
expect(
|
|
205
205
|
screen.getByTestId('ucl-uikit-confidential-input-mask')
|
|
206
|
-
).toHaveTextContent('
|
|
206
|
+
).toHaveTextContent('●●-●●-72');
|
|
207
207
|
});
|
|
208
208
|
|
|
209
209
|
test('uses native password masking when visibleWhenFocused is false', async () => {
|
|
@@ -219,7 +219,7 @@ describe('ConfidentialInput', () => {
|
|
|
219
219
|
) as HTMLInputElement;
|
|
220
220
|
expect(
|
|
221
221
|
screen.getByTestId('ucl-uikit-confidential-input-mask')
|
|
222
|
-
).toHaveTextContent('
|
|
222
|
+
).toHaveTextContent('●●-●●-72');
|
|
223
223
|
|
|
224
224
|
await user.click(input);
|
|
225
225
|
|
|
@@ -234,7 +234,7 @@ describe('ConfidentialInput', () => {
|
|
|
234
234
|
|
|
235
235
|
expect(
|
|
236
236
|
screen.getByTestId('ucl-uikit-confidential-input-mask')
|
|
237
|
-
).toHaveTextContent('
|
|
237
|
+
).toHaveTextContent('●●-●●-72');
|
|
238
238
|
});
|
|
239
239
|
|
|
240
240
|
test('shows the raw value while editing a showable value', async () => {
|
|
@@ -250,7 +250,7 @@ describe('ConfidentialInput', () => {
|
|
|
250
250
|
) as HTMLInputElement;
|
|
251
251
|
expect(
|
|
252
252
|
screen.getByTestId('ucl-uikit-confidential-input-mask')
|
|
253
|
-
).toHaveTextContent('
|
|
253
|
+
).toHaveTextContent('●●-●●-72');
|
|
254
254
|
|
|
255
255
|
await user.click(input);
|
|
256
256
|
|
|
@@ -268,7 +268,7 @@ describe('ConfidentialInput', () => {
|
|
|
268
268
|
|
|
269
269
|
expect(
|
|
270
270
|
screen.getByTestId('ucl-uikit-confidential-input-mask')
|
|
271
|
-
).toHaveTextContent('
|
|
271
|
+
).toHaveTextContent('●●-●●-72');
|
|
272
272
|
});
|
|
273
273
|
|
|
274
274
|
test('supports a right icon alongside the visibility control', () => {
|
|
@@ -293,7 +293,7 @@ describe('ConfidentialInput', () => {
|
|
|
293
293
|
const user = userEvent.setup();
|
|
294
294
|
renderInput({
|
|
295
295
|
value: '123472',
|
|
296
|
-
maskFormat: '
|
|
296
|
+
maskFormat: '**-**-##',
|
|
297
297
|
format: '##-##-##',
|
|
298
298
|
showable: true,
|
|
299
299
|
onChange: () => {},
|
|
@@ -301,7 +301,7 @@ describe('ConfidentialInput', () => {
|
|
|
301
301
|
|
|
302
302
|
expect(
|
|
303
303
|
screen.getByTestId('ucl-uikit-confidential-input-mask')
|
|
304
|
-
).toHaveTextContent('
|
|
304
|
+
).toHaveTextContent('●●-●●-72');
|
|
305
305
|
|
|
306
306
|
await user.click(
|
|
307
307
|
screen.getByRole('button', { name: 'Show confidential value' })
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
export type MaskFormat = string | ((value: string) => string);
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
interface FormatConfidentialValueOptions {
|
|
4
|
+
includeTemplateLiterals?: boolean;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const applyMaskTemplate = (
|
|
8
|
+
value: string,
|
|
9
|
+
template: string,
|
|
10
|
+
includeTemplateLiterals: boolean
|
|
11
|
+
) => {
|
|
4
12
|
const valueCharacters = Array.from(value);
|
|
5
13
|
let valueIndex = 0;
|
|
6
14
|
let maskedValue = '';
|
|
@@ -14,7 +22,7 @@ const applyMaskTemplate = (value: string, template: string) => {
|
|
|
14
22
|
} else if (templateCharacter === '*') {
|
|
15
23
|
maskedValue += '*';
|
|
16
24
|
valueIndex += 1;
|
|
17
|
-
} else {
|
|
25
|
+
} else if (includeTemplateLiterals) {
|
|
18
26
|
maskedValue += templateCharacter;
|
|
19
27
|
}
|
|
20
28
|
}
|
|
@@ -24,13 +32,17 @@ const applyMaskTemplate = (value: string, template: string) => {
|
|
|
24
32
|
);
|
|
25
33
|
};
|
|
26
34
|
|
|
27
|
-
const formatConfidentialValue = (
|
|
35
|
+
const formatConfidentialValue = (
|
|
36
|
+
value: string,
|
|
37
|
+
maskFormat: MaskFormat,
|
|
38
|
+
{ includeTemplateLiterals = true }: FormatConfidentialValueOptions = {}
|
|
39
|
+
) => {
|
|
28
40
|
const maskedValue =
|
|
29
41
|
typeof maskFormat === 'function'
|
|
30
42
|
? maskFormat(value)
|
|
31
|
-
: applyMaskTemplate(value, maskFormat);
|
|
43
|
+
: applyMaskTemplate(value, maskFormat, includeTemplateLiterals);
|
|
32
44
|
|
|
33
|
-
return maskedValue.split('*').join('
|
|
45
|
+
return maskedValue.split('*').join('●');
|
|
34
46
|
};
|
|
35
47
|
|
|
36
48
|
export default formatConfidentialValue;
|