uikit-react-public 0.49.0 → 0.49.1
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/DisplayField/DisplayField.d.ts +3 -0
- package/dist/components/DisplayField/DisplayField.stories.d.ts +3 -0
- package/dist/components/Input/Input.d.ts +2 -0
- package/dist/components/Input/Input.stories.d.ts +1 -0
- package/dist/components/common/formatDisplayValue.d.ts +3 -0
- package/dist/index.js +4974 -4894
- package/lib/components/ConfidentialDisplayField/ConfidentialDisplayField.stories.tsx +3 -2
- package/lib/components/ConfidentialDisplayField/ConfidentialDisplayField.tsx +2 -0
- package/lib/components/ConfidentialDisplayField/Documentation.mdx +6 -3
- package/lib/components/ConfidentialDisplayField/__tests__/ConfidentialDisplayField.test.tsx +25 -6
- package/lib/components/ConfidentialInput/ConfidentialInput.stories.tsx +2 -2
- package/lib/components/ConfidentialInput/ConfidentialInput.tsx +13 -5
- package/lib/components/ConfidentialInput/Documentation.mdx +4 -2
- package/lib/components/ConfidentialInput/__tests__/ConfidentialInput.test.tsx +33 -7
- package/lib/components/DisplayField/DisplayField.stories.tsx +22 -0
- package/lib/components/DisplayField/DisplayField.tsx +20 -8
- package/lib/components/DisplayField/Documentation.mdx +9 -0
- package/lib/components/DisplayField/__tests__/DisplayField.test.tsx +36 -1
- package/lib/components/Input/Documentation.mdx +11 -0
- package/lib/components/Input/Input.stories.tsx +7 -0
- package/lib/components/Input/Input.tsx +134 -2
- package/lib/components/Input/__tests__/Input.test.tsx +96 -1
- package/lib/components/common/formatConfidentialValue.ts +1 -1
- package/lib/components/common/formatDisplayValue.ts +29 -0
- package/package.json +1 -1
|
@@ -10,7 +10,7 @@ const meta = {
|
|
|
10
10
|
tags: ['autodocs'],
|
|
11
11
|
args: {
|
|
12
12
|
value: '12345678',
|
|
13
|
-
maskFormat: '
|
|
13
|
+
maskFormat: '******##',
|
|
14
14
|
},
|
|
15
15
|
} satisfies Meta<typeof ConfidentialDisplayField>;
|
|
16
16
|
|
|
@@ -29,7 +29,8 @@ export const Showable: Story = {
|
|
|
29
29
|
export const WithSeparators: Story = {
|
|
30
30
|
args: {
|
|
31
31
|
value: '123472',
|
|
32
|
-
maskFormat: '
|
|
32
|
+
maskFormat: '****##',
|
|
33
|
+
format: '##-##-##',
|
|
33
34
|
showable: true,
|
|
34
35
|
},
|
|
35
36
|
};
|
|
@@ -22,6 +22,7 @@ const ConfidentialDisplayField = forwardRef<Ref, ConfidentialDisplayFieldProps>(
|
|
|
22
22
|
(
|
|
23
23
|
{
|
|
24
24
|
value,
|
|
25
|
+
numeric,
|
|
25
26
|
maskFormat,
|
|
26
27
|
showable = false,
|
|
27
28
|
testId = NAME,
|
|
@@ -67,6 +68,7 @@ const ConfidentialDisplayField = forwardRef<Ref, ConfidentialDisplayFieldProps>(
|
|
|
67
68
|
{...props}
|
|
68
69
|
ref={ref}
|
|
69
70
|
value={displayedValue}
|
|
71
|
+
numeric={numeric ?? typeof value === 'number'}
|
|
70
72
|
testId={testId}
|
|
71
73
|
noMargins
|
|
72
74
|
/>
|
|
@@ -13,13 +13,14 @@ import {
|
|
|
13
13
|
<Subtitle>A DisplayField that masks confidential values</Subtitle>
|
|
14
14
|
|
|
15
15
|
`ConfidentialDisplayField` extends `DisplayField` with the same simple mask
|
|
16
|
-
format used by `ConfidentialInput`.
|
|
16
|
+
format used by `ConfidentialInput`. `#` reveals a value character, `*` masks it
|
|
17
17
|
with a `•`, and other characters are displayed as separators.
|
|
18
18
|
|
|
19
19
|
<Source
|
|
20
20
|
code={`<ConfidentialDisplayField
|
|
21
|
-
value='
|
|
22
|
-
maskFormat='
|
|
21
|
+
value='123472'
|
|
22
|
+
maskFormat='****##'
|
|
23
|
+
format='##-##-##'
|
|
23
24
|
showable
|
|
24
25
|
/>`}
|
|
25
26
|
/>
|
|
@@ -31,6 +32,8 @@ with a `•`, and other characters are displayed as separators.
|
|
|
31
32
|
or formatter.
|
|
32
33
|
- `showable`: **boolean** - Adds a button that toggles the original value.
|
|
33
34
|
Defaults to `false`.
|
|
35
|
+
- `format`: **string | (value: string) => string** - Formats both the masked
|
|
36
|
+
and visible values using the `DisplayField` format syntax.
|
|
34
37
|
- All `DisplayField` props are supported.
|
|
35
38
|
|
|
36
39
|
<ArgTypes />
|
|
@@ -18,7 +18,7 @@ describe('ConfidentialDisplayField', () => {
|
|
|
18
18
|
test('applies a string mask', () => {
|
|
19
19
|
renderDisplayField({
|
|
20
20
|
value: '12345678',
|
|
21
|
-
maskFormat: '
|
|
21
|
+
maskFormat: '******##',
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
expect(
|
|
@@ -40,7 +40,7 @@ describe('ConfidentialDisplayField', () => {
|
|
|
40
40
|
test('does not add a visibility control by default', () => {
|
|
41
41
|
renderDisplayField({
|
|
42
42
|
value: '12345678',
|
|
43
|
-
maskFormat: '
|
|
43
|
+
maskFormat: '******##',
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
expect(
|
|
@@ -52,7 +52,7 @@ describe('ConfidentialDisplayField', () => {
|
|
|
52
52
|
const user = userEvent.setup();
|
|
53
53
|
renderDisplayField({
|
|
54
54
|
value: '12345678',
|
|
55
|
-
maskFormat: '
|
|
55
|
+
maskFormat: '******##',
|
|
56
56
|
showable: true,
|
|
57
57
|
});
|
|
58
58
|
|
|
@@ -72,6 +72,25 @@ describe('ConfidentialDisplayField', () => {
|
|
|
72
72
|
expect(output).toHaveTextContent('••••••78');
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
+
test('formats both masked and visible values', async () => {
|
|
76
|
+
const user = userEvent.setup();
|
|
77
|
+
renderDisplayField({
|
|
78
|
+
value: '123472',
|
|
79
|
+
maskFormat: '****##',
|
|
80
|
+
format: '##-##-##',
|
|
81
|
+
showable: true,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const output = screen.getByTestId('ucl-uikit-confidential-display-field');
|
|
85
|
+
expect(output).toHaveTextContent('••-••-72');
|
|
86
|
+
|
|
87
|
+
await user.click(
|
|
88
|
+
screen.getByRole('button', { name: 'Show confidential value' })
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
expect(output).toHaveTextContent('12-34-72');
|
|
92
|
+
});
|
|
93
|
+
|
|
75
94
|
test('forwards its ref to the output', () => {
|
|
76
95
|
const ref = createRef<HTMLOutputElement>();
|
|
77
96
|
render(
|
|
@@ -79,7 +98,7 @@ describe('ConfidentialDisplayField', () => {
|
|
|
79
98
|
<ConfidentialDisplayField
|
|
80
99
|
ref={ref}
|
|
81
100
|
value='12345678'
|
|
82
|
-
maskFormat='
|
|
101
|
+
maskFormat='******##'
|
|
83
102
|
/>
|
|
84
103
|
</ThemeContextProvider>
|
|
85
104
|
);
|
|
@@ -95,7 +114,7 @@ describe('ConfidentialDisplayField', () => {
|
|
|
95
114
|
<ThemeContextProvider>
|
|
96
115
|
<ConfidentialDisplayField
|
|
97
116
|
value='12345678'
|
|
98
|
-
maskFormat='
|
|
117
|
+
maskFormat='******##'
|
|
99
118
|
showable
|
|
100
119
|
/>
|
|
101
120
|
</ThemeContextProvider>
|
|
@@ -112,7 +131,7 @@ describe('ConfidentialDisplayField', () => {
|
|
|
112
131
|
<ThemeContextProvider>
|
|
113
132
|
<ConfidentialDisplayField
|
|
114
133
|
value='12345678'
|
|
115
|
-
maskFormat='
|
|
134
|
+
maskFormat='******##'
|
|
116
135
|
showable={false}
|
|
117
136
|
/>
|
|
118
137
|
</ThemeContextProvider>
|
|
@@ -11,7 +11,7 @@ const meta = {
|
|
|
11
11
|
args: {
|
|
12
12
|
'aria-label': 'Account number',
|
|
13
13
|
defaultValue: '12345678',
|
|
14
|
-
maskFormat: '
|
|
14
|
+
maskFormat: '****####',
|
|
15
15
|
},
|
|
16
16
|
} satisfies Meta<typeof ConfidentialInput>;
|
|
17
17
|
|
|
@@ -31,7 +31,7 @@ export const WithSeparators: Story = {
|
|
|
31
31
|
args: {
|
|
32
32
|
'aria-label': 'Sort code',
|
|
33
33
|
defaultValue: '123472',
|
|
34
|
-
maskFormat: '
|
|
34
|
+
maskFormat: '**-**-##',
|
|
35
35
|
showable: true,
|
|
36
36
|
},
|
|
37
37
|
};
|
|
@@ -19,6 +19,7 @@ import marginsStyle from '../common/marginsStyle';
|
|
|
19
19
|
import formatConfidentialValue, {
|
|
20
20
|
MaskFormat,
|
|
21
21
|
} from '../common/formatConfidentialValue';
|
|
22
|
+
import formatDisplayValue from '../common/formatDisplayValue';
|
|
22
23
|
|
|
23
24
|
export const NAME = 'ucl-uikit-confidential-input';
|
|
24
25
|
|
|
@@ -46,6 +47,7 @@ const ConfidentialInput = forwardRef<Ref, ConfidentialInputProps>(
|
|
|
46
47
|
maskFormat,
|
|
47
48
|
showable = false,
|
|
48
49
|
visibleWhenFocused = true,
|
|
50
|
+
format,
|
|
49
51
|
value,
|
|
50
52
|
defaultValue,
|
|
51
53
|
disabled,
|
|
@@ -125,8 +127,14 @@ const ConfidentialInput = forwardRef<Ref, ConfidentialInputProps>(
|
|
|
125
127
|
stringValue,
|
|
126
128
|
maskFormat
|
|
127
129
|
);
|
|
128
|
-
const
|
|
129
|
-
!
|
|
130
|
+
const configuredValueIsVisible =
|
|
131
|
+
!isFocused &&
|
|
132
|
+
stringValue.length > 0 &&
|
|
133
|
+
(!valueIsPersistentlyShown || Boolean(format));
|
|
134
|
+
const configuredValue = formatDisplayValue(
|
|
135
|
+
valueIsPersistentlyShown ? stringValue : displayedMaskedValue,
|
|
136
|
+
format
|
|
137
|
+
);
|
|
130
138
|
|
|
131
139
|
const wrapperStyle = cx(
|
|
132
140
|
NAME,
|
|
@@ -232,7 +240,7 @@ const ConfidentialInput = forwardRef<Ref, ConfidentialInputProps>(
|
|
|
232
240
|
iconPosition={iconPosition}
|
|
233
241
|
iconButton={visibilityButton}
|
|
234
242
|
inputClassName={cx(
|
|
235
|
-
|
|
243
|
+
configuredValueIsVisible && hiddenInputStyle,
|
|
236
244
|
inputClassName
|
|
237
245
|
)}
|
|
238
246
|
testId={testId}
|
|
@@ -241,13 +249,13 @@ const ConfidentialInput = forwardRef<Ref, ConfidentialInputProps>(
|
|
|
241
249
|
onFocus={handleFocus}
|
|
242
250
|
onBlur={handleBlur}
|
|
243
251
|
/>
|
|
244
|
-
{
|
|
252
|
+
{configuredValueIsVisible && (
|
|
245
253
|
<span
|
|
246
254
|
className={maskStyle}
|
|
247
255
|
data-testid={`${testId}-mask`}
|
|
248
256
|
aria-hidden='true'
|
|
249
257
|
>
|
|
250
|
-
{
|
|
258
|
+
{configuredValue}
|
|
251
259
|
</span>
|
|
252
260
|
)}
|
|
253
261
|
</span>
|
|
@@ -19,7 +19,7 @@ change events, and form submission while displaying a masked value.
|
|
|
19
19
|
|
|
20
20
|
## Usage
|
|
21
21
|
|
|
22
|
-
The string mask is a simple template.
|
|
22
|
+
The string mask is a simple template. `#` reveals the corresponding value
|
|
23
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,
|
|
@@ -32,7 +32,7 @@ value. The configured mask returns on blur.
|
|
|
32
32
|
<Source
|
|
33
33
|
code={`<ConfidentialInput
|
|
34
34
|
defaultValue='12345678'
|
|
35
|
-
maskFormat='
|
|
35
|
+
maskFormat='****####'
|
|
36
36
|
showable
|
|
37
37
|
aria-label='Account number'
|
|
38
38
|
/>`}
|
|
@@ -58,6 +58,8 @@ A function can be supplied when the template syntax is not sufficient:
|
|
|
58
58
|
- `visibleWhenFocused`: **boolean** - Reveals the original value while focused.
|
|
59
59
|
When `false`, the native password mask is shown while editing. Defaults to
|
|
60
60
|
`true`.
|
|
61
|
+
- `format`: **string | (value: string) => string** - Formats both the masked
|
|
62
|
+
and visible values while blurred using the `Input` format syntax.
|
|
61
63
|
- All `Input` props except `type` and `iconButton` are supported.
|
|
62
64
|
|
|
63
65
|
<ArgTypes />
|
|
@@ -17,7 +17,7 @@ const renderInput = (props: React.ComponentProps<typeof ConfidentialInput>) =>
|
|
|
17
17
|
describe('ConfidentialInput', () => {
|
|
18
18
|
test('applies a string mask without changing the input value', () => {
|
|
19
19
|
renderInput({
|
|
20
|
-
maskFormat: '
|
|
20
|
+
maskFormat: '####****',
|
|
21
21
|
value: '12345678',
|
|
22
22
|
onChange: () => {},
|
|
23
23
|
});
|
|
@@ -32,7 +32,7 @@ describe('ConfidentialInput', () => {
|
|
|
32
32
|
|
|
33
33
|
test('supports separators and masks characters beyond the template', () => {
|
|
34
34
|
renderInput({
|
|
35
|
-
maskFormat: '
|
|
35
|
+
maskFormat: '**-**-##',
|
|
36
36
|
value: '1234729',
|
|
37
37
|
onChange: () => {},
|
|
38
38
|
});
|
|
@@ -68,7 +68,7 @@ describe('ConfidentialInput', () => {
|
|
|
68
68
|
test('toggles between the masked and shown value', async () => {
|
|
69
69
|
const user = userEvent.setup();
|
|
70
70
|
renderInput({
|
|
71
|
-
maskFormat: '
|
|
71
|
+
maskFormat: '****####',
|
|
72
72
|
defaultValue: '12345678',
|
|
73
73
|
showable: true,
|
|
74
74
|
});
|
|
@@ -131,7 +131,7 @@ describe('ConfidentialInput', () => {
|
|
|
131
131
|
<form data-testid='form'>
|
|
132
132
|
<ConfidentialInput
|
|
133
133
|
name='accountNumber'
|
|
134
|
-
maskFormat='
|
|
134
|
+
maskFormat='****####'
|
|
135
135
|
defaultValue='12345678'
|
|
136
136
|
/>
|
|
137
137
|
</form>
|
|
@@ -180,7 +180,7 @@ describe('ConfidentialInput', () => {
|
|
|
180
180
|
test('shows the raw value while editing a non-showable value', async () => {
|
|
181
181
|
const user = userEvent.setup();
|
|
182
182
|
renderInput({
|
|
183
|
-
maskFormat: '
|
|
183
|
+
maskFormat: '**-**-##',
|
|
184
184
|
defaultValue: '123472',
|
|
185
185
|
});
|
|
186
186
|
|
|
@@ -209,7 +209,7 @@ describe('ConfidentialInput', () => {
|
|
|
209
209
|
test('uses native password masking when visibleWhenFocused is false', async () => {
|
|
210
210
|
const user = userEvent.setup();
|
|
211
211
|
renderInput({
|
|
212
|
-
maskFormat: '
|
|
212
|
+
maskFormat: '**-**-##',
|
|
213
213
|
defaultValue: '123472',
|
|
214
214
|
visibleWhenFocused: false,
|
|
215
215
|
});
|
|
@@ -240,7 +240,7 @@ describe('ConfidentialInput', () => {
|
|
|
240
240
|
test('shows the raw value while editing a showable value', async () => {
|
|
241
241
|
const user = userEvent.setup();
|
|
242
242
|
renderInput({
|
|
243
|
-
maskFormat: '
|
|
243
|
+
maskFormat: '**-**-##',
|
|
244
244
|
defaultValue: '123472',
|
|
245
245
|
showable: true,
|
|
246
246
|
});
|
|
@@ -289,6 +289,32 @@ describe('ConfidentialInput', () => {
|
|
|
289
289
|
).toHaveStyle({ right: '16px' });
|
|
290
290
|
});
|
|
291
291
|
|
|
292
|
+
test('formats both masked and visible values while blurred', async () => {
|
|
293
|
+
const user = userEvent.setup();
|
|
294
|
+
renderInput({
|
|
295
|
+
value: '123472',
|
|
296
|
+
maskFormat: '****##',
|
|
297
|
+
format: '##-##-##',
|
|
298
|
+
showable: true,
|
|
299
|
+
onChange: () => {},
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
expect(
|
|
303
|
+
screen.getByTestId('ucl-uikit-confidential-input-mask')
|
|
304
|
+
).toHaveTextContent('••-••-72');
|
|
305
|
+
|
|
306
|
+
await user.click(
|
|
307
|
+
screen.getByRole('button', { name: 'Show confidential value' })
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
expect(
|
|
311
|
+
screen.getByTestId('ucl-uikit-confidential-input-mask')
|
|
312
|
+
).toHaveTextContent('12-34-72');
|
|
313
|
+
expect(screen.getByTestId('ucl-uikit-confidential-input')).toHaveValue(
|
|
314
|
+
'123472'
|
|
315
|
+
);
|
|
316
|
+
});
|
|
317
|
+
|
|
292
318
|
test('disables the visibility control with the input', () => {
|
|
293
319
|
renderInput({
|
|
294
320
|
maskFormat: '****',
|
|
@@ -24,3 +24,25 @@ export const Number: Story = {
|
|
|
24
24
|
value: 12345678,
|
|
25
25
|
},
|
|
26
26
|
};
|
|
27
|
+
|
|
28
|
+
export const NumericString: Story = {
|
|
29
|
+
args: {
|
|
30
|
+
value: '00123456',
|
|
31
|
+
numeric: true,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const StringFormat: Story = {
|
|
36
|
+
args: {
|
|
37
|
+
value: '112233',
|
|
38
|
+
numeric: true,
|
|
39
|
+
format: '##-##-##',
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const FunctionFormat: Story = {
|
|
44
|
+
args: {
|
|
45
|
+
value: 1234.5,
|
|
46
|
+
format: (value) => `£${value}`,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
@@ -3,6 +3,9 @@ import { css, cx } from '@emotion/css';
|
|
|
3
3
|
import useTheme from '../../theme/useTheme';
|
|
4
4
|
import { FieldContext } from '../Field';
|
|
5
5
|
import marginsStyle, { MarginProps } from '../common/marginsStyle';
|
|
6
|
+
import formatDisplayValue, {
|
|
7
|
+
DisplayValueFormat,
|
|
8
|
+
} from '../common/formatDisplayValue';
|
|
6
9
|
|
|
7
10
|
export const NAME = 'ucl-uikit-display-field';
|
|
8
11
|
|
|
@@ -11,6 +14,8 @@ export interface DisplayFieldBaseProps extends Omit<
|
|
|
11
14
|
'children' | 'value'
|
|
12
15
|
> {
|
|
13
16
|
value: string | number;
|
|
17
|
+
numeric?: boolean;
|
|
18
|
+
format?: DisplayValueFormat;
|
|
14
19
|
testId?: string;
|
|
15
20
|
}
|
|
16
21
|
|
|
@@ -19,21 +24,28 @@ export type DisplayFieldProps = DisplayFieldBaseProps & MarginProps;
|
|
|
19
24
|
export type Ref = HTMLOutputElement;
|
|
20
25
|
|
|
21
26
|
const DisplayField = forwardRef<Ref, DisplayFieldProps>(
|
|
22
|
-
(
|
|
27
|
+
(
|
|
28
|
+
{ value, numeric = false, format, testId = NAME, className, ...props },
|
|
29
|
+
ref
|
|
30
|
+
) => {
|
|
23
31
|
const [theme] = useTheme();
|
|
24
32
|
const { id: contextId } = use(FieldContext);
|
|
25
33
|
const id = props.id ?? contextId;
|
|
26
|
-
const
|
|
34
|
+
const useNumericTypography = typeof value === 'number' || numeric;
|
|
35
|
+
const typography = useNumericTypography
|
|
36
|
+
? theme.typography.body.mdNumeric
|
|
37
|
+
: theme.typography.body.md;
|
|
38
|
+
const displayedValue = formatDisplayValue(String(value), format);
|
|
27
39
|
|
|
28
40
|
const baseStyle = css`
|
|
29
41
|
display: block;
|
|
30
42
|
min-height: 24px;
|
|
31
43
|
color: ${theme.colour.text.default};
|
|
32
|
-
font-family: ${
|
|
33
|
-
font-feature-settings: ${
|
|
34
|
-
font-size: ${
|
|
35
|
-
font-weight: ${
|
|
36
|
-
line-height: ${
|
|
44
|
+
font-family: ${typography.fontFamily};
|
|
45
|
+
font-feature-settings: ${typography.fontSettings};
|
|
46
|
+
font-size: ${typography.fontSize}px;
|
|
47
|
+
font-weight: ${typography.fontWeight};
|
|
48
|
+
line-height: ${typography.lineHeight}%;
|
|
37
49
|
overflow-wrap: anywhere;
|
|
38
50
|
`;
|
|
39
51
|
|
|
@@ -47,7 +59,7 @@ const DisplayField = forwardRef<Ref, DisplayFieldProps>(
|
|
|
47
59
|
className={style}
|
|
48
60
|
data-testid={testId}
|
|
49
61
|
>
|
|
50
|
-
{
|
|
62
|
+
{displayedValue}
|
|
51
63
|
</output>
|
|
52
64
|
);
|
|
53
65
|
}
|
|
@@ -25,6 +25,12 @@ to display a record in the same field structure used by editable forms.
|
|
|
25
25
|
## Props
|
|
26
26
|
|
|
27
27
|
- `value`: **string | number** - The value to display.
|
|
28
|
+
- `numeric`: **boolean** - Applies numeric typography to string values. Number
|
|
29
|
+
values use numeric typography automatically. Defaults to `false`.
|
|
30
|
+
- `format`: **string | (value: string) => string** - Formats the displayed
|
|
31
|
+
value. In a string template, each `#` consumes one value character and other
|
|
32
|
+
characters are displayed literally. For example, `##-##-##` formats `112233`
|
|
33
|
+
as `11-22-33`.
|
|
28
34
|
- All standard HTML `<output>` props are supported.
|
|
29
35
|
|
|
30
36
|
<ArgTypes />
|
|
@@ -33,3 +39,6 @@ to display a record in the same field structure used by editable forms.
|
|
|
33
39
|
|
|
34
40
|
<Canvas of={DisplayFieldStories.Text} />
|
|
35
41
|
<Canvas of={DisplayFieldStories.Number} />
|
|
42
|
+
<Canvas of={DisplayFieldStories.NumericString} />
|
|
43
|
+
<Canvas of={DisplayFieldStories.StringFormat} />
|
|
44
|
+
<Canvas of={DisplayFieldStories.FunctionFormat} />
|
|
@@ -25,8 +25,43 @@ describe('DisplayField', () => {
|
|
|
25
25
|
test('displays a numeric value', () => {
|
|
26
26
|
renderDisplayField({ value: 12345678 });
|
|
27
27
|
|
|
28
|
+
const output = screen.getByTestId('ucl-uikit-display-field');
|
|
29
|
+
expect(output).toHaveTextContent('12345678');
|
|
30
|
+
expect(getComputedStyle(output).fontFamily).toContain('DM Mono');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('uses numeric typography for a string when numeric is true', () => {
|
|
34
|
+
renderDisplayField({ value: '00123456', numeric: true });
|
|
35
|
+
|
|
36
|
+
expect(
|
|
37
|
+
getComputedStyle(screen.getByTestId('ucl-uikit-display-field')).fontFamily
|
|
38
|
+
).toContain('DM Mono');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('formats a value with a string template', () => {
|
|
42
|
+
renderDisplayField({ value: '112233', format: '##-##-##' });
|
|
43
|
+
|
|
44
|
+
expect(screen.getByTestId('ucl-uikit-display-field')).toHaveTextContent(
|
|
45
|
+
'11-22-33'
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('appends value characters beyond a string template', () => {
|
|
50
|
+
renderDisplayField({ value: '11223344', format: '##-##-##' });
|
|
51
|
+
|
|
52
|
+
expect(screen.getByTestId('ucl-uikit-display-field')).toHaveTextContent(
|
|
53
|
+
'11-22-3344'
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('formats a value with a function', () => {
|
|
58
|
+
renderDisplayField({
|
|
59
|
+
value: 1234.5,
|
|
60
|
+
format: (value) => `£${value}`,
|
|
61
|
+
});
|
|
62
|
+
|
|
28
63
|
expect(screen.getByTestId('ucl-uikit-display-field')).toHaveTextContent(
|
|
29
|
-
'
|
|
64
|
+
'£1234.5'
|
|
30
65
|
);
|
|
31
66
|
});
|
|
32
67
|
|
|
@@ -17,6 +17,7 @@ export const usage = {
|
|
|
17
17
|
rightIcon: `<Input icon={<Icon.Settings />} iconPosition='right' type='password' />`,
|
|
18
18
|
password: `<Input type='password' />`,
|
|
19
19
|
email: `<Input type='email' icon={<Icon.Facebook />} iconPosition='left' />`,
|
|
20
|
+
formatted: `<Input defaultValue='112233' format='##-##-##' />`,
|
|
20
21
|
};
|
|
21
22
|
|
|
22
23
|
<Meta of={InputStories} />
|
|
@@ -47,6 +48,10 @@ Use the Input component wherever standard inputs are required with optional enha
|
|
|
47
48
|
- `type`: **string** – Input type (e.g. `text`, `password`, `email`, etc.)
|
|
48
49
|
- `disabled`: **boolean** – Disables the input field
|
|
49
50
|
- `placeholder`: **string** – Placeholder text
|
|
51
|
+
- `format`: **string | (value: string) => string** – Formats the displayed
|
|
52
|
+
value while the input is blurred. `#` consumes one raw value character;
|
|
53
|
+
other template characters are displayed literally. The raw value is shown
|
|
54
|
+
while focused and remains unchanged for events and form submission.
|
|
50
55
|
- All standard HTML `<input>` props are supported
|
|
51
56
|
|
|
52
57
|
# Variants
|
|
@@ -92,3 +97,9 @@ Use the Input component wherever standard inputs are required with optional enha
|
|
|
92
97
|
sourceState='shown'
|
|
93
98
|
source={{ code: usage.email }}
|
|
94
99
|
/>
|
|
100
|
+
|
|
101
|
+
<Canvas
|
|
102
|
+
of={InputStories.Formatted}
|
|
103
|
+
sourceState='shown'
|
|
104
|
+
source={{ code: usage.formatted }}
|
|
105
|
+
/>
|