uikit-react-public 0.48.1 → 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.
Files changed (37) hide show
  1. package/dist/components/ConfidentialDisplayField/ConfidentialDisplayField.d.ts +10 -0
  2. package/dist/components/ConfidentialDisplayField/ConfidentialDisplayField.stories.d.ts +19 -0
  3. package/dist/components/ConfidentialDisplayField/__tests__/ConfidentialDisplayField.test.d.ts +1 -0
  4. package/dist/components/ConfidentialDisplayField/index.d.ts +2 -0
  5. package/dist/components/ConfidentialInput/ConfidentialInput.d.ts +2 -1
  6. package/dist/components/DisplayField/DisplayField.d.ts +14 -0
  7. package/dist/components/DisplayField/DisplayField.stories.d.ts +19 -0
  8. package/dist/components/DisplayField/__tests__/DisplayField.test.d.ts +1 -0
  9. package/dist/components/DisplayField/index.d.ts +2 -0
  10. package/dist/components/Input/Input.d.ts +2 -0
  11. package/dist/components/Input/Input.stories.d.ts +1 -0
  12. package/dist/components/common/formatConfidentialValue.d.ts +3 -0
  13. package/dist/components/common/formatDisplayValue.d.ts +3 -0
  14. package/dist/components/index.d.ts +4 -0
  15. package/dist/index.js +5941 -5764
  16. package/lib/components/ConfidentialDisplayField/ConfidentialDisplayField.stories.tsx +43 -0
  17. package/lib/components/ConfidentialDisplayField/ConfidentialDisplayField.tsx +105 -0
  18. package/lib/components/ConfidentialDisplayField/Documentation.mdx +46 -0
  19. package/lib/components/ConfidentialDisplayField/__tests__/ConfidentialDisplayField.test.tsx +144 -0
  20. package/lib/components/ConfidentialDisplayField/index.ts +2 -0
  21. package/lib/components/ConfidentialInput/ConfidentialInput.stories.tsx +2 -2
  22. package/lib/components/ConfidentialInput/ConfidentialInput.tsx +21 -35
  23. package/lib/components/ConfidentialInput/Documentation.mdx +4 -2
  24. package/lib/components/ConfidentialInput/__tests__/ConfidentialInput.test.tsx +33 -7
  25. package/lib/components/DisplayField/DisplayField.stories.tsx +48 -0
  26. package/lib/components/DisplayField/DisplayField.tsx +68 -0
  27. package/lib/components/DisplayField/Documentation.mdx +44 -0
  28. package/lib/components/DisplayField/__tests__/DisplayField.test.tsx +96 -0
  29. package/lib/components/DisplayField/index.ts +2 -0
  30. package/lib/components/Input/Documentation.mdx +11 -0
  31. package/lib/components/Input/Input.stories.tsx +7 -0
  32. package/lib/components/Input/Input.tsx +134 -2
  33. package/lib/components/Input/__tests__/Input.test.tsx +96 -1
  34. package/lib/components/common/formatConfidentialValue.ts +36 -0
  35. package/lib/components/common/formatDisplayValue.ts +29 -0
  36. package/lib/components/index.ts +6 -0
  37. package/package.json +1 -1
@@ -0,0 +1,43 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import ConfidentialDisplayField from './ConfidentialDisplayField';
3
+
4
+ const meta = {
5
+ title: 'Components/ConfidentialDisplayField',
6
+ component: ConfidentialDisplayField,
7
+ parameters: {
8
+ layout: 'centered',
9
+ },
10
+ tags: ['autodocs'],
11
+ args: {
12
+ value: '12345678',
13
+ maskFormat: '******##',
14
+ },
15
+ } satisfies Meta<typeof ConfidentialDisplayField>;
16
+
17
+ export default meta;
18
+
19
+ type Story = StoryObj<typeof meta>;
20
+
21
+ export const Default: Story = {};
22
+
23
+ export const Showable: Story = {
24
+ args: {
25
+ showable: true,
26
+ },
27
+ };
28
+
29
+ export const WithSeparators: Story = {
30
+ args: {
31
+ value: '123472',
32
+ maskFormat: '****##',
33
+ format: '##-##-##',
34
+ showable: true,
35
+ },
36
+ };
37
+
38
+ export const FunctionMask: Story = {
39
+ args: {
40
+ maskFormat: (value) =>
41
+ `${'*'.repeat(Math.max(0, value.length - 2))}${value.slice(-2)}`,
42
+ },
43
+ };
@@ -0,0 +1,105 @@
1
+ import { forwardRef, memo, useEffect, useState } from 'react';
2
+ import { css, cx } from '@emotion/css';
3
+ import useTheme from '../../theme/useTheme';
4
+ import DisplayField, { DisplayFieldProps } from '../DisplayField';
5
+ import Icon from '../Icon';
6
+ import IconButton from '../IconButton';
7
+ import formatConfidentialValue, {
8
+ MaskFormat,
9
+ } from '../common/formatConfidentialValue';
10
+ import marginsStyle from '../common/marginsStyle';
11
+
12
+ export const NAME = 'ucl-uikit-confidential-display-field';
13
+
14
+ export interface ConfidentialDisplayFieldProps extends DisplayFieldProps {
15
+ maskFormat: MaskFormat;
16
+ showable?: boolean;
17
+ }
18
+
19
+ export type Ref = HTMLOutputElement;
20
+
21
+ const ConfidentialDisplayField = forwardRef<Ref, ConfidentialDisplayFieldProps>(
22
+ (
23
+ {
24
+ value,
25
+ numeric,
26
+ maskFormat,
27
+ showable = false,
28
+ testId = NAME,
29
+ className,
30
+ m,
31
+ mv,
32
+ mh,
33
+ mt,
34
+ mb,
35
+ ml,
36
+ mr,
37
+ noMargins,
38
+ ...props
39
+ },
40
+ ref
41
+ ) => {
42
+ const [theme] = useTheme();
43
+ const [isShown, setIsShown] = useState(false);
44
+ const stringValue = String(value);
45
+ const valueIsShown = showable && isShown;
46
+ const displayedValue = valueIsShown
47
+ ? stringValue
48
+ : formatConfidentialValue(stringValue, maskFormat);
49
+
50
+ useEffect(() => {
51
+ if (!showable) setIsShown(false);
52
+ }, [showable]);
53
+
54
+ const wrapperStyle = cx(
55
+ NAME,
56
+ css`
57
+ display: inline-flex;
58
+ align-items: center;
59
+ gap: ${theme.padding.p8};
60
+ `,
61
+ marginsStyle({ m, mv, mh, mt, mb, ml, mr, noMargins }, theme),
62
+ className
63
+ );
64
+
65
+ return (
66
+ <span className={wrapperStyle}>
67
+ <DisplayField
68
+ {...props}
69
+ ref={ref}
70
+ value={displayedValue}
71
+ numeric={numeric ?? typeof value === 'number'}
72
+ testId={testId}
73
+ noMargins
74
+ />
75
+ {showable && (
76
+ <IconButton
77
+ type='button'
78
+ aria-label={
79
+ valueIsShown
80
+ ? 'Hide confidential value'
81
+ : 'Show confidential value'
82
+ }
83
+ aria-pressed={valueIsShown}
84
+ testId={`${testId}-visibility-toggle`}
85
+ onClick={() => setIsShown((shown) => !shown)}
86
+ >
87
+ {valueIsShown ? (
88
+ <Icon.EyeOff
89
+ aria-hidden='true'
90
+ testId={`${testId}-hide-icon`}
91
+ />
92
+ ) : (
93
+ <Icon.Eye
94
+ aria-hidden='true'
95
+ testId={`${testId}-show-icon`}
96
+ />
97
+ )}
98
+ </IconButton>
99
+ )}
100
+ </span>
101
+ );
102
+ }
103
+ );
104
+
105
+ export default memo(ConfidentialDisplayField);
@@ -0,0 +1,46 @@
1
+ import * as ConfidentialDisplayFieldStories from './ConfidentialDisplayField.stories';
2
+ import {
3
+ Meta,
4
+ Title,
5
+ Subtitle,
6
+ Canvas,
7
+ Source,
8
+ ArgTypes,
9
+ } from '@storybook/addon-docs/blocks';
10
+
11
+ <Meta of={ConfidentialDisplayFieldStories} />
12
+ <Title />
13
+ <Subtitle>A DisplayField that masks confidential values</Subtitle>
14
+
15
+ `ConfidentialDisplayField` extends `DisplayField` with the same simple mask
16
+ format used by `ConfidentialInput`. `#` reveals a value character, `*` masks it
17
+ with a `•`, and other characters are displayed as separators.
18
+
19
+ <Source
20
+ code={`<ConfidentialDisplayField
21
+ value='123472'
22
+ maskFormat='****##'
23
+ format='##-##-##'
24
+ showable
25
+ />`}
26
+ />
27
+
28
+ ## Props
29
+
30
+ - `value`: **string | number** - The original value.
31
+ - `maskFormat`: **string | (value: string) => string** - Required mask template
32
+ or formatter.
33
+ - `showable`: **boolean** - Adds a button that toggles the original value.
34
+ Defaults to `false`.
35
+ - `format`: **string | (value: string) => string** - Formats both the masked
36
+ and visible values using the `DisplayField` format syntax.
37
+ - All `DisplayField` props are supported.
38
+
39
+ <ArgTypes />
40
+
41
+ ## Examples
42
+
43
+ <Canvas of={ConfidentialDisplayFieldStories.Default} />
44
+ <Canvas of={ConfidentialDisplayFieldStories.Showable} />
45
+ <Canvas of={ConfidentialDisplayFieldStories.WithSeparators} />
46
+ <Canvas of={ConfidentialDisplayFieldStories.FunctionMask} />
@@ -0,0 +1,144 @@
1
+ import { createRef } from 'react';
2
+ import { describe, expect, test } from 'vitest';
3
+ import { render, screen } from '@testing-library/react';
4
+ import userEvent from '@testing-library/user-event';
5
+ import { ThemeContextProvider } from '../../../theme/useTheme';
6
+ import ConfidentialDisplayField from '../ConfidentialDisplayField';
7
+
8
+ const renderDisplayField = (
9
+ props: React.ComponentProps<typeof ConfidentialDisplayField>
10
+ ) =>
11
+ render(
12
+ <ThemeContextProvider>
13
+ <ConfidentialDisplayField {...props} />
14
+ </ThemeContextProvider>
15
+ );
16
+
17
+ describe('ConfidentialDisplayField', () => {
18
+ test('applies a string mask', () => {
19
+ renderDisplayField({
20
+ value: '12345678',
21
+ maskFormat: '******##',
22
+ });
23
+
24
+ expect(
25
+ screen.getByTestId('ucl-uikit-confidential-display-field')
26
+ ).toHaveTextContent('••••••78');
27
+ });
28
+
29
+ test('applies a function mask', () => {
30
+ renderDisplayField({
31
+ value: 12345678,
32
+ maskFormat: (value) => `**** ending in ${value.slice(-2)}`,
33
+ });
34
+
35
+ expect(
36
+ screen.getByTestId('ucl-uikit-confidential-display-field')
37
+ ).toHaveTextContent('•••• ending in 78');
38
+ });
39
+
40
+ test('does not add a visibility control by default', () => {
41
+ renderDisplayField({
42
+ value: '12345678',
43
+ maskFormat: '******##',
44
+ });
45
+
46
+ expect(
47
+ screen.queryByRole('button', { name: 'Show confidential value' })
48
+ ).not.toBeInTheDocument();
49
+ });
50
+
51
+ test('toggles the original value', async () => {
52
+ const user = userEvent.setup();
53
+ renderDisplayField({
54
+ value: '12345678',
55
+ maskFormat: '******##',
56
+ showable: true,
57
+ });
58
+
59
+ const output = screen.getByTestId('ucl-uikit-confidential-display-field');
60
+ await user.click(
61
+ screen.getByRole('button', { name: 'Show confidential value' })
62
+ );
63
+
64
+ expect(output).toHaveTextContent('12345678');
65
+ expect(
66
+ screen.getByRole('button', { name: 'Hide confidential value' })
67
+ ).toHaveAttribute('aria-pressed', 'true');
68
+
69
+ await user.click(
70
+ screen.getByRole('button', { name: 'Hide confidential value' })
71
+ );
72
+ expect(output).toHaveTextContent('••••••78');
73
+ });
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
+
94
+ test('forwards its ref to the output', () => {
95
+ const ref = createRef<HTMLOutputElement>();
96
+ render(
97
+ <ThemeContextProvider>
98
+ <ConfidentialDisplayField
99
+ ref={ref}
100
+ value='12345678'
101
+ maskFormat='******##'
102
+ />
103
+ </ThemeContextProvider>
104
+ );
105
+
106
+ expect(ref.current).toBe(
107
+ screen.getByTestId('ucl-uikit-confidential-display-field')
108
+ );
109
+ });
110
+
111
+ test('hides the value when showable changes to false', async () => {
112
+ const user = userEvent.setup();
113
+ const { rerender } = render(
114
+ <ThemeContextProvider>
115
+ <ConfidentialDisplayField
116
+ value='12345678'
117
+ maskFormat='******##'
118
+ showable
119
+ />
120
+ </ThemeContextProvider>
121
+ );
122
+
123
+ await user.click(
124
+ screen.getByRole('button', { name: 'Show confidential value' })
125
+ );
126
+ expect(
127
+ screen.getByTestId('ucl-uikit-confidential-display-field')
128
+ ).toHaveTextContent('12345678');
129
+
130
+ rerender(
131
+ <ThemeContextProvider>
132
+ <ConfidentialDisplayField
133
+ value='12345678'
134
+ maskFormat='******##'
135
+ showable={false}
136
+ />
137
+ </ThemeContextProvider>
138
+ );
139
+
140
+ expect(
141
+ screen.getByTestId('ucl-uikit-confidential-display-field')
142
+ ).toHaveTextContent('••••••78');
143
+ });
144
+ });
@@ -0,0 +1,2 @@
1
+ export { default } from './ConfidentialDisplayField';
2
+ export type { ConfidentialDisplayFieldProps } from './ConfidentialDisplayField';
@@ -11,7 +11,7 @@ const meta = {
11
11
  args: {
12
12
  'aria-label': 'Account number',
13
13
  defaultValue: '12345678',
14
- maskFormat: '****1111',
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: '**-**-11',
34
+ maskFormat: '**-**-##',
35
35
  showable: true,
36
36
  },
37
37
  };
@@ -16,6 +16,10 @@ import IconButton from '../IconButton';
16
16
  import Input, { InputProps } from '../Input';
17
17
  import { FieldContext } from '../Field';
18
18
  import marginsStyle from '../common/marginsStyle';
19
+ import formatConfidentialValue, {
20
+ MaskFormat,
21
+ } from '../common/formatConfidentialValue';
22
+ import formatDisplayValue from '../common/formatDisplayValue';
19
23
 
20
24
  export const NAME = 'ucl-uikit-confidential-input';
21
25
 
@@ -23,7 +27,7 @@ export interface ConfidentialInputProps extends Omit<
23
27
  InputProps,
24
28
  'iconButton' | 'type'
25
29
  > {
26
- maskFormat: string | ((value: string) => string);
30
+ maskFormat: MaskFormat;
27
31
  showable?: boolean;
28
32
  visibleWhenFocused?: boolean;
29
33
  }
@@ -37,36 +41,13 @@ const getStringValue = (
37
41
  return Array.isArray(value) ? value.join(',') : String(value);
38
42
  };
39
43
 
40
- const applyMaskTemplate = (value: string, template: string) => {
41
- const valueCharacters = Array.from(value);
42
- let valueIndex = 0;
43
- let maskedValue = '';
44
-
45
- for (const templateCharacter of Array.from(template)) {
46
- if (valueIndex >= valueCharacters.length) break;
47
-
48
- if (templateCharacter === '1') {
49
- maskedValue += valueCharacters[valueIndex];
50
- valueIndex += 1;
51
- } else if (templateCharacter === '*') {
52
- maskedValue += '*';
53
- valueIndex += 1;
54
- } else {
55
- maskedValue += templateCharacter;
56
- }
57
- }
58
-
59
- return (
60
- maskedValue + '*'.repeat(Math.max(0, valueCharacters.length - valueIndex))
61
- );
62
- };
63
-
64
44
  const ConfidentialInput = forwardRef<Ref, ConfidentialInputProps>(
65
45
  (
66
46
  {
67
47
  maskFormat,
68
48
  showable = false,
69
49
  visibleWhenFocused = true,
50
+ format,
70
51
  value,
71
52
  defaultValue,
72
53
  disabled,
@@ -142,13 +123,18 @@ const ConfidentialInput = forwardRef<Ref, ConfidentialInputProps>(
142
123
 
143
124
  const stringValue =
144
125
  value === undefined ? uncontrolledValue : getStringValue(value);
145
- const maskedValue =
146
- typeof maskFormat === 'function'
147
- ? maskFormat(stringValue)
148
- : applyMaskTemplate(stringValue, maskFormat);
149
- const displayedMaskedValue = maskedValue.split('*').join('•');
150
- const configuredMaskIsVisible =
151
- !valueIsPersistentlyShown && !isFocused && stringValue.length > 0;
126
+ const displayedMaskedValue = formatConfidentialValue(
127
+ stringValue,
128
+ maskFormat
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
+ );
152
138
 
153
139
  const wrapperStyle = cx(
154
140
  NAME,
@@ -254,7 +240,7 @@ const ConfidentialInput = forwardRef<Ref, ConfidentialInputProps>(
254
240
  iconPosition={iconPosition}
255
241
  iconButton={visibilityButton}
256
242
  inputClassName={cx(
257
- configuredMaskIsVisible && hiddenInputStyle,
243
+ configuredValueIsVisible && hiddenInputStyle,
258
244
  inputClassName
259
245
  )}
260
246
  testId={testId}
@@ -263,13 +249,13 @@ const ConfidentialInput = forwardRef<Ref, ConfidentialInputProps>(
263
249
  onFocus={handleFocus}
264
250
  onBlur={handleBlur}
265
251
  />
266
- {configuredMaskIsVisible && (
252
+ {configuredValueIsVisible && (
267
253
  <span
268
254
  className={maskStyle}
269
255
  data-testid={`${testId}-mask`}
270
256
  aria-hidden='true'
271
257
  >
272
- {displayedMaskedValue}
258
+ {configuredValue}
273
259
  </span>
274
260
  )}
275
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. `1` reveals the corresponding value
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='****1111'
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: '1111****',
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: '**-**-11',
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: '****1111',
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='****1111'
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: '**-**-11',
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: '**-**-11',
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: '**-**-11',
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: '****',
@@ -0,0 +1,48 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import DisplayField from './DisplayField';
3
+
4
+ const meta = {
5
+ title: 'Components/DisplayField',
6
+ component: DisplayField,
7
+ parameters: {
8
+ layout: 'centered',
9
+ },
10
+ tags: ['autodocs'],
11
+ args: {
12
+ value: 'Personal savings',
13
+ },
14
+ } satisfies Meta<typeof DisplayField>;
15
+
16
+ export default meta;
17
+
18
+ type Story = StoryObj<typeof meta>;
19
+
20
+ export const Text: Story = {};
21
+
22
+ export const Number: Story = {
23
+ args: {
24
+ value: 12345678,
25
+ },
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
+ };