@transferwise/components 0.0.0-experimental-f304e2c → 0.0.0-experimental-2fa5e3f

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 (84) hide show
  1. package/build/card/Card.js +20 -13
  2. package/build/card/Card.js.map +1 -1
  3. package/build/card/Card.mjs +21 -14
  4. package/build/card/Card.mjs.map +1 -1
  5. package/build/common/Option/Option.js.map +1 -1
  6. package/build/common/Option/Option.mjs.map +1 -1
  7. package/build/dateLookup/dateTrigger/DateTrigger.js +4 -8
  8. package/build/dateLookup/dateTrigger/DateTrigger.js.map +1 -1
  9. package/build/dateLookup/dateTrigger/DateTrigger.mjs +4 -8
  10. package/build/dateLookup/dateTrigger/DateTrigger.mjs.map +1 -1
  11. package/build/field/Field.js +3 -13
  12. package/build/field/Field.js.map +1 -1
  13. package/build/field/Field.mjs +3 -13
  14. package/build/field/Field.mjs.map +1 -1
  15. package/build/i18n/en.json +1 -3
  16. package/build/i18n/en.json.js +1 -3
  17. package/build/i18n/en.json.js.map +1 -1
  18. package/build/i18n/en.json.mjs +1 -3
  19. package/build/i18n/en.json.mjs.map +1 -1
  20. package/build/inlineAlert/InlineAlert.js +6 -4
  21. package/build/inlineAlert/InlineAlert.js.map +1 -1
  22. package/build/inlineAlert/InlineAlert.mjs +6 -4
  23. package/build/inlineAlert/InlineAlert.mjs.map +1 -1
  24. package/build/label/Label.js +1 -29
  25. package/build/label/Label.js.map +1 -1
  26. package/build/label/Label.mjs +2 -30
  27. package/build/label/Label.mjs.map +1 -1
  28. package/build/main.css +8 -4
  29. package/build/styles/dateLookup/dateTrigger/DateTrigger.css +8 -0
  30. package/build/styles/main.css +8 -4
  31. package/build/types/card/Card.d.ts.map +1 -1
  32. package/build/types/common/Option/Option.d.ts +2 -0
  33. package/build/types/common/Option/Option.d.ts.map +1 -1
  34. package/build/types/dateLookup/dateTrigger/DateTrigger.d.ts.map +1 -1
  35. package/build/types/field/Field.d.ts +2 -4
  36. package/build/types/field/Field.d.ts.map +1 -1
  37. package/build/types/index.d.ts +1 -1
  38. package/build/types/index.d.ts.map +1 -1
  39. package/build/types/inlineAlert/InlineAlert.d.ts.map +1 -1
  40. package/build/types/label/Label.d.ts +1 -10
  41. package/build/types/label/Label.d.ts.map +1 -1
  42. package/package.json +3 -3
  43. package/src/card/Card.spec.tsx +35 -3
  44. package/src/card/Card.story.tsx +56 -40
  45. package/src/card/Card.tsx +32 -14
  46. package/src/common/Option/Option.tsx +2 -0
  47. package/src/dateInput/DateInput.tests.story.tsx +31 -7
  48. package/src/dateLookup/DateLookup.rtl.spec.tsx +1 -1
  49. package/src/dateLookup/dateTrigger/DateTrigger.css +8 -0
  50. package/src/dateLookup/dateTrigger/DateTrigger.less +8 -0
  51. package/src/dateLookup/dateTrigger/DateTrigger.spec.js +1 -1
  52. package/src/dateLookup/dateTrigger/DateTrigger.tsx +4 -9
  53. package/src/field/Field.spec.tsx +3 -3
  54. package/src/field/Field.story.tsx +7 -106
  55. package/src/field/Field.tests.story.tsx +33 -0
  56. package/src/field/Field.tsx +6 -12
  57. package/src/i18n/en.json +1 -3
  58. package/src/index.ts +1 -1
  59. package/src/inlineAlert/InlineAlert.story.tsx +72 -8
  60. package/src/inlineAlert/InlineAlert.tsx +3 -5
  61. package/src/inputs/InputGroup.spec.tsx +1 -1
  62. package/src/inputs/SearchInput.spec.tsx +1 -1
  63. package/src/inputs/SelectInput.spec.tsx +1 -1
  64. package/src/label/Label.story.tsx +25 -19
  65. package/src/label/Label.tsx +2 -38
  66. package/src/main.css +8 -4
  67. package/src/main.less +0 -1
  68. package/src/moneyInput/MoneyInput.story.tsx +2 -0
  69. package/src/radioGroup/RadioGroup.rtl.spec.tsx +1 -1
  70. package/src/select/Select.rtl.spec.tsx +1 -1
  71. package/src/switch/Switch.spec.tsx +1 -1
  72. package/build/label/Label.messages.js +0 -15
  73. package/build/label/Label.messages.js.map +0 -1
  74. package/build/label/Label.messages.mjs +0 -13
  75. package/build/label/Label.messages.mjs.map +0 -1
  76. package/build/styles/field/Field.css +0 -4
  77. package/build/types/label/Label.messages.d.ts +0 -12
  78. package/build/types/label/Label.messages.d.ts.map +0 -1
  79. package/build/types/label/index.d.ts +0 -3
  80. package/build/types/label/index.d.ts.map +0 -1
  81. package/src/field/Field.css +0 -4
  82. package/src/field/Field.less +0 -5
  83. package/src/label/Label.messages.tsx +0 -12
  84. package/src/label/index.ts +0 -2
package/src/card/Card.tsx CHANGED
@@ -1,5 +1,5 @@
1
1
  import { clsx } from 'clsx';
2
- import { forwardRef } from 'react';
2
+ import { forwardRef, useId } from 'react';
3
3
 
4
4
  import Body from '../body';
5
5
  import Chevron from '../chevron';
@@ -40,13 +40,16 @@ const Card = forwardRef(function Card(
40
40
  }: CardProps,
41
41
  reference,
42
42
  ) {
43
- const isOpen = !!(isExpanded && children);
43
+ const labelId = useId();
44
+ const contentId = useId();
45
+ const isExpandable = Boolean(children);
46
+
44
47
 
45
48
  return (
46
49
  <Element
47
50
  ref={reference}
48
51
  className={clsx('np-card', className, {
49
- 'np-card--expanded': isOpen,
52
+ 'np-card--expanded': isExpandable && isExpanded,
50
53
  'np-card--inactive': !children,
51
54
  'np-card--has-icon': !!icon,
52
55
  })}
@@ -54,23 +57,38 @@ const Card = forwardRef(function Card(
54
57
  data-testid={dataTestId}
55
58
  >
56
59
  <Option
60
+ aria-expanded={isExpandable ? isExpanded : undefined}
61
+ aria-controls={isExpanded ? contentId : undefined}
62
+ id={labelId}
57
63
  aria-label={ariaLabel}
58
- as={children ? 'button' : 'div'}
59
- className={clsx('np-card__button')}
64
+ as={isExpandable ? 'button' : 'div'}
65
+ className="np-card__button"
60
66
  media={icon}
61
67
  title={title}
62
68
  content={details}
63
69
  showMediaAtAllSizes
64
- button={children && <Chevron orientation={isOpen ? Position.TOP : Position.BOTTOM} />}
65
- onClick={children ? () => onClick?.(!isExpanded) : undefined}
66
- />
67
- <div
68
- className={clsx('np-card__divider', {
69
- 'np-card__divider--expanded': isOpen,
70
- })}
70
+ button={
71
+ isExpandable ? <Chevron orientation={isExpanded ? Position.TOP : Position.BOTTOM} /> : null
72
+ }
73
+ onClick={isExpandable ? () => onClick?.(!isExpanded) : undefined}
71
74
  />
72
- {isOpen && (
73
- <Body as="span" type={Typography.BODY_LARGE} className="np-card__content d-block">
75
+
76
+ {isExpandable && (
77
+ <div
78
+ className={clsx('np-card__divider', {
79
+ 'np-card__divider--expanded': isExpanded,
80
+ })}
81
+ />
82
+ )}
83
+
84
+ {isExpandable && isExpanded && (
85
+ <Body
86
+ as="div"
87
+ type={Typography.BODY_LARGE}
88
+ className={clsx('np-card__content', isExpanded ? 'd-block' : 'hide')}
89
+ id={contentId}
90
+ aria-labelledby={labelId}
91
+ >
74
92
  {children}
75
93
  </Body>
76
94
  )}
@@ -6,6 +6,8 @@ import { LinkProps } from '../commonProps';
6
6
 
7
7
  export interface BaseOptionProps extends Omit<HTMLAttributes<HTMLElement>, 'title' | 'content'> {
8
8
  'aria-label'?: string;
9
+ 'aria-expanded'?: boolean;
10
+ 'aria-controls'?: string;
9
11
  media?: ReactNode;
10
12
  name?: string;
11
13
  htmlFor?: string;
@@ -1,7 +1,7 @@
1
1
  import { Meta, StoryObj } from '@storybook/react';
2
2
  import { userEvent, within, fn } from '@storybook/test';
3
3
 
4
- import { DateInput, Field, InlineAlert, Label } from '..';
4
+ import { DateInput, DateMode, Info, InlineAlert, Title, Typography } from '..';
5
5
  import { lorem10, storyConfig } from '../test-utils';
6
6
 
7
7
  import Provider from '../provider/Provider';
@@ -39,19 +39,43 @@ export const WithLabel = {
39
39
  },
40
40
  render: (args) => {
41
41
  const id1 = 'date-input-group-label-1';
42
+ const label = 'Date of Birth';
42
43
 
43
44
  return (
44
45
  <>
45
- <Label id={id1}>
46
- <Label.Optional>Date of Delivery</Label.Optional>
47
- </Label>
46
+ <Title type={Typography.TITLE_SUBSECTION}>
47
+ label (as <code>div</code> element) is linked with <code>DateInput</code> via{' '}
48
+ <code>aria-labelledby</code> prop
49
+ </Title>
50
+ <div className="control-label" id={id1}>
51
+ Date of Delivery
52
+ </div>
48
53
  <DateInput {...args} aria-labelledby={id1} />
49
54
 
50
55
  <br />
51
56
 
52
- <Field label="Date of Delivery">
53
- <DateInput {...args} />
54
- </Field>
57
+ <Title type={Typography.TITLE_SUBSECTION}>
58
+ label (as <code>div</code> element) is detached but <code>DateInput</code> has same label
59
+ via <code>aria-label</code> attribute
60
+ </Title>
61
+ <div className="control-label">
62
+ {label}{' '}
63
+ <Info aria-label="Fast transfer hint" title="Fast transfer hint" content={lorem10} />
64
+ </div>
65
+ <DateInput {...args} aria-label={label} />
66
+
67
+ <br />
68
+
69
+ <Title type={Typography.TITLE_SUBSECTION}>
70
+ <code>DateInput</code> wrapped in <code>fieldset</code> + using <code>legend</code> as
71
+ label (rare use case)
72
+ </Title>
73
+ <fieldset>
74
+ <legend className="control-label">
75
+ Expiry Date for Credit Card (example of MONTH_YEAR mode)
76
+ </legend>
77
+ <DateInput {...args} mode={DateMode.MONTH_YEAR} />
78
+ </fieldset>
55
79
  </>
56
80
  );
57
81
  },
@@ -28,7 +28,7 @@ describe('DateLookup', () => {
28
28
  </Field>,
29
29
  );
30
30
  // TODO: Replace with `.toHaveAttribute('aria-haspopup')`
31
- expect(screen.getByLabelText(/Date of birth/)).toHaveTextContent(
31
+ expect(screen.getByLabelText('Date of birth')).toHaveTextContent(
32
32
  initialValue.getFullYear().toString(),
33
33
  );
34
34
  });
@@ -4,10 +4,18 @@
4
4
  white-space: nowrap;
5
5
  width: 100%;
6
6
  }
7
+ .np-date-trigger .control-label {
8
+ font-weight: 400;
9
+ font-weight: var(--font-weight-regular);
10
+ }
7
11
  .np-theme-personal .np-date-trigger {
8
12
  padding-left: 16px;
9
13
  padding-left: var(--size-16);
10
14
  }
15
+ .np-theme-personal .np-date-trigger .control-label + span {
16
+ font-weight: 400;
17
+ font-weight: var(--font-weight-regular);
18
+ }
11
19
  .clear-btn {
12
20
  transition: color 0.15s ease-in-out;
13
21
  color: #c9cbce;
@@ -7,8 +7,16 @@
7
7
  white-space: nowrap;
8
8
  width: 100%;
9
9
 
10
+ .control-label {
11
+ font-weight: var(--font-weight-regular);
12
+ }
13
+
10
14
  .np-theme-personal & {
11
15
  padding-left: var(--size-16);
16
+
17
+ .control-label + span {
18
+ font-weight: var(--font-weight-regular);
19
+ }
12
20
  }
13
21
  }
14
22
 
@@ -119,5 +119,5 @@ describe('DateTrigger', () => {
119
119
  const button = () => component.find('.np-date-trigger');
120
120
  const clearButton = () => component.find('.clear-btn');
121
121
  const chevron = () => component.find(Chevron);
122
- const label = () => component.find('.np-date-trigger-label');
122
+ const label = () => component.find('.control-label');
123
123
  });
@@ -2,13 +2,12 @@ import { formatDate } from '@transferwise/formatting';
2
2
  import { useIntl } from 'react-intl';
3
3
 
4
4
  import Chevron from '../../chevron';
5
- import { Size, Position, SizeSmall, SizeMedium, SizeLarge, Typography } from '../../common';
5
+ import { Size, Position, SizeSmall, SizeMedium, SizeLarge } from '../../common';
6
6
  import { CloseButton } from '../../common/closeButton/CloseButton';
7
7
 
8
8
  import messages from './DateTrigger.messages';
9
9
  import { useContext } from 'react';
10
10
  import { OverlayIdContext } from '../../provider/overlay/OverlayIdProvider';
11
- import Body from '../../body';
12
11
 
13
12
  interface DateTriggerProps {
14
13
  selectedDate: Date | null;
@@ -46,19 +45,15 @@ const DateTrigger: React.FC<DateTriggerProps> = ({
46
45
  type="button"
47
46
  onClick={onClick}
48
47
  >
49
- {label && (
50
- <Body as="span" className="np-date-trigger-label m-r-1">
51
- {label}
52
- </Body>
53
- )}
48
+ {label && <span className="control-label small m-r-1">{label}</span>}
54
49
  {selectedDate ? (
55
- <Body as="span" type={Typography.BODY_LARGE}>
50
+ <span className="font-weight-normal">
56
51
  {formatDate(selectedDate, locale, {
57
52
  day: 'numeric',
58
53
  month: monthFormat,
59
54
  year: 'numeric',
60
55
  })}
61
- </Body>
56
+ </span>
62
57
  ) : (
63
58
  <span
64
59
  className="form-control-placeholder visible-xs-inline visible-sm-inline
@@ -14,7 +14,7 @@ describe('Field', () => {
14
14
  </Field>,
15
15
  );
16
16
 
17
- expect(screen.getByLabelText(/Phone number/)).toBeInTheDocument();
17
+ expect(screen.getByLabelText('Phone number')).toBeInTheDocument();
18
18
  expect(screen.getByRole('textbox')).not.toHaveAttribute('aria-describedby');
19
19
  });
20
20
 
@@ -25,7 +25,7 @@ describe('Field', () => {
25
25
  </Field>,
26
26
  );
27
27
 
28
- const textbox = screen.getByText('This is help text');
28
+ const textbox = screen.getByRole('textbox', { description: 'This is help text' });
29
29
  expect(textbox).toBeInTheDocument();
30
30
  expect(textbox).not.toBeInvalid();
31
31
  });
@@ -50,7 +50,7 @@ describe('Field', () => {
50
50
  );
51
51
 
52
52
  expect(screen.getByRole('textbox', { description: 'This is error text' })).toBeInTheDocument();
53
- expect(screen.getByText('This is help text')).toBeInTheDocument();
53
+ expect(screen.queryByText('This is help text')).not.toBeInTheDocument();
54
54
  });
55
55
 
56
56
  it('avoids triggering button within label inadvertently', async () => {
@@ -1,15 +1,8 @@
1
- /* eslint-disable jsx-a11y/label-has-associated-control */
2
1
  import { useState } from 'react';
3
2
 
4
3
  import { Input } from '../inputs/Input';
5
4
  import { Field } from './Field';
6
5
  import { Sentiment } from '../common';
7
- import DateInput from '../dateInput';
8
- import { fn } from '@storybook/test';
9
- import { lorem10, lorem40 } from '../test-utils';
10
- import { TextArea } from '../inputs/TextArea';
11
- import { SearchInput } from '../inputs/SearchInput';
12
- import Info from '../info';
13
6
 
14
7
  export default {
15
8
  component: Field,
@@ -20,66 +13,9 @@ export default {
20
13
  export const Basic = () => {
21
14
  const [value, setValue] = useState<string | undefined>('This is some text');
22
15
  return (
23
- <div className="row">
24
- <div className="col-md-8 col-md-offset-2">
25
- <Field label="Required Text Input" required>
26
- <Input value={value} onChange={({ target }) => setValue(target.value)} />
27
- </Field>
28
- <Field label="Text Input">
29
- <Input value={value} onChange={({ target }) => setValue(target.value)} />
30
- </Field>
31
- <Field label="Text Input with Description" description="This a field Description">
32
- <Input value={value} onChange={({ target }) => setValue(target.value)} />
33
- </Field>
34
- <Field
35
- label="Text Input with Validation Error"
36
- description="This a field Description"
37
- sentiment={Sentiment.NEGATIVE}
38
- message="Validation error, please take a look"
39
- >
40
- <Input value={value} onChange={({ target }) => setValue(target.value)} />
41
- </Field>
42
-
43
- <Field label="Date Of Birth">
44
- <DateInput onChange={fn} />
45
- </Field>
46
-
47
- <Field label="Date Of Birth with Description" description={lorem10}>
48
- <DateInput onChange={fn} />
49
- </Field>
50
-
51
- <Field label="Search business">
52
- <SearchInput />
53
- </Field>
54
-
55
- <Field label="Textarea">
56
- <TextArea />
57
- </Field>
58
-
59
- <Field label="Textarea with Description" description="This a TextArea Description">
60
- <TextArea />
61
- </Field>
62
-
63
- <Field
64
- label="Textarea with Validation Error"
65
- description="This a TextArea Description"
66
- message={lorem10}
67
- sentiment="negative"
68
- >
69
- <TextArea />
70
- </Field>
71
-
72
- <Field
73
- label={
74
- <>
75
- Label with Suffix <Info content={lorem40} />
76
- </>
77
- }
78
- >
79
- <TextArea />
80
- </Field>
81
- </div>
82
- </div>
16
+ <Field label="Phone number">
17
+ <Input value={value} onChange={({ target }) => setValue(target.value)} />
18
+ </Field>
83
19
  );
84
20
  };
85
21
 
@@ -87,51 +23,16 @@ export const WithStatusMessages = () => {
87
23
  const [value, setValue] = useState<string | undefined>('This is some text');
88
24
  return (
89
25
  <div>
90
- <Field
91
- label="Text Input with Positive Message"
92
- sentiment={Sentiment.POSITIVE}
93
- message="Positive message"
94
- >
26
+ <Field label="Phone number" sentiment={Sentiment.POSITIVE} message="Positive message">
95
27
  <Input value={value} onChange={({ target }) => setValue(target.value)} />
96
28
  </Field>
97
-
98
- <Field
99
- label="Text Input with Warning Message"
100
- sentiment={Sentiment.WARNING}
101
- message="Warning message"
102
- >
29
+ <Field label="Phone number" sentiment={Sentiment.WARNING} message="Warning message">
103
30
  <Input value={value} onChange={({ target }) => setValue(target.value)} />
104
31
  </Field>
105
-
106
- <Field
107
- label="Text Input with Validation Error"
108
- sentiment={Sentiment.NEGATIVE}
109
- message="This is a required field"
110
- >
111
- <Input value={value} onChange={({ target }) => setValue(target.value)} />
112
- </Field>
113
-
114
- {/* instance with deprecated `hint` prop */}
115
- <Field
116
- label="Text Input with deprecated `hint` prop"
117
- hint="This is a helpful message"
118
- sentiment={Sentiment.NEGATIVE}
119
- message="Validation error, please take a look"
120
- >
32
+ <Field label="Phone number" sentiment={Sentiment.NEGATIVE} message="This is a required field">
121
33
  <Input value={value} onChange={({ target }) => setValue(target.value)} />
122
34
  </Field>
123
-
124
- {/* instance with deprecated `error` & `hint` props */}
125
- <Field
126
- label="Text Input with deprecated `error` & `hint` props"
127
- hint="This is a helpful message"
128
- error="Validation error, please take a look"
129
- >
130
- <Input value={value} onChange={({ target }) => setValue(target.value)} />
131
- </Field>
132
-
133
- {/* instance of info via `message` property, this is rare case (e.g MoneyInput) */}
134
- <Field label="Text Input with Info under the field" message="This is a helpful message">
35
+ <Field label="Phone number" message="This is a helpful message">
135
36
  <Input value={value} onChange={({ target }) => setValue(target.value)} />
136
37
  </Field>
137
38
  </div>
@@ -0,0 +1,33 @@
1
+ import { useState } from 'react';
2
+
3
+ import { Input } from '../inputs/Input';
4
+ import { Field } from './Field';
5
+ import { Sentiment } from '../common';
6
+
7
+ export default {
8
+ component: Field,
9
+ title: 'Field/Tests',
10
+ };
11
+
12
+ export const WithHelpAndErrorOnBlur = () => {
13
+ const [value, setValue] = useState('This is some text');
14
+ const [error, setError] = useState<string | undefined>(undefined);
15
+ return (
16
+ <Field
17
+ label="Phone number"
18
+ sentiment={error ? Sentiment.NEGATIVE : Sentiment.NEUTRAL}
19
+ message={error || 'Please include country code'}
20
+ >
21
+ <Input
22
+ value={value}
23
+ onChange={({ target }) => {
24
+ setValue(target.value);
25
+ setError(undefined);
26
+ }}
27
+ onBlur={() => {
28
+ setError('Something went wrong');
29
+ }}
30
+ />
31
+ </Field>
32
+ );
33
+ };
@@ -9,18 +9,16 @@ import {
9
9
  InputIdContextProvider,
10
10
  InputInvalidProvider,
11
11
  } from '../inputs/contexts';
12
- import { Label } from '../label';
12
+ import { Label } from '../label/Label';
13
13
 
14
14
  export type FieldProps = {
15
15
  /** `null` disables auto-generating the `id` attribute, falling back to nesting-based label association over setting `htmlFor` explicitly. */
16
16
  id?: string | null;
17
17
  /** Should be specified unless the wrapped control has its own labeling mechanism, e.g. `Checkbox`. */
18
18
  label?: React.ReactNode;
19
- required?: boolean;
20
- /** @deprecated use `description` prop instead */
19
+ /** @deprecated use `message` and `type={Sentiment.NEUTRAL}` prop instead */
21
20
  hint?: React.ReactNode;
22
21
  message?: React.ReactNode;
23
- description?: React.ReactNode;
24
22
  /** @deprecated use `message` and `type={Sentiment.NEGATIVE}` prop instead */
25
23
  error?: React.ReactNode;
26
24
  sentiment?: `${Sentiment.NEGATIVE | Sentiment.NEUTRAL | Sentiment.POSITIVE | Sentiment.WARNING}`;
@@ -31,17 +29,14 @@ export type FieldProps = {
31
29
  export const Field = ({
32
30
  id,
33
31
  label,
34
- required = false,
35
32
  message: propMessage,
36
- hint,
37
- description = hint,
38
33
  sentiment: propType = Sentiment.NEUTRAL,
39
34
  className,
40
35
  children,
41
36
  ...props
42
37
  }: FieldProps) => {
43
38
  const sentiment = props.error ? Sentiment.NEGATIVE : propType;
44
- const message = propMessage || props.error;
39
+ const message = props.error || props.hint || propMessage;
45
40
  const hasError = sentiment === Sentiment.NEGATIVE;
46
41
 
47
42
  const labelId = useId();
@@ -58,7 +53,7 @@ export const Field = ({
58
53
  <InputInvalidProvider value={hasError}>
59
54
  <div
60
55
  className={clsx(
61
- 'np-field form-group d-block',
56
+ 'form-group d-block',
62
57
  {
63
58
  'has-success': sentiment === Sentiment.POSITIVE,
64
59
  'has-warning': sentiment === Sentiment.WARNING,
@@ -70,9 +65,8 @@ export const Field = ({
70
65
  >
71
66
  {label != null ? (
72
67
  <Label id={labelId} htmlFor={inputId}>
73
- {required ? label : <Label.Optional>{label}</Label.Optional>}
74
- {description && <Label.Description>{description}</Label.Description>}
75
- <div className="np-field-control">{children}</div>
68
+ {label}
69
+ {children}
76
70
  </Label>
77
71
  ) : (
78
72
  children
package/src/i18n/en.json CHANGED
@@ -18,7 +18,6 @@
18
18
  "neptune.DateLookup.year": "year",
19
19
  "neptune.FlowNavigation.back": "back to previous step",
20
20
  "neptune.Info.ariaLabel": "More information",
21
- "neptune.Label.optional": "(Optional)",
22
21
  "neptune.Link.opensInNewTab": "(opens in new tab)",
23
22
  "neptune.MoneyInput.Select.placeholder": "Select an option...",
24
23
  "neptune.PhoneNumberInput.SelectInput.placeholder": "Select an option...",
@@ -56,6 +55,5 @@
56
55
  "neptune.UploadItem.uploaded": "Uploaded",
57
56
  "neptune.UploadItem.uploadedFile": "Uploaded file",
58
57
  "neptune.UploadItem.uploading": "Uploading...",
59
- "neptune.UploadItem.uploadingFailed": "Uploading failed",
60
- "neptune.aria.Label.optional": "This field is optional"
58
+ "neptune.UploadItem.uploadingFailed": "Uploading failed"
61
59
  }
package/src/index.ts CHANGED
@@ -44,7 +44,7 @@ export type {
44
44
  } from './inputs/SelectInput';
45
45
  export type { TextAreaProps } from './inputs/TextArea';
46
46
  export type { InstructionsListProps } from './instructionsList';
47
- export type { LabelProps, LabelOptionalProps, LabelDescriptionProps } from './label/Label';
47
+ export type { LabelProps } from './label/Label';
48
48
  export type { LoaderProps } from './loader';
49
49
  export type { MarkdownProps } from './markdown';
50
50
  export type { ModalProps } from './modal';
@@ -2,9 +2,9 @@ import { select, text } from '@storybook/addon-knobs';
2
2
  import { Meta } from '@storybook/react';
3
3
 
4
4
  import { Sentiment } from '../common';
5
+ import { Input } from '../inputs/Input';
5
6
 
6
7
  import InlineAlert, { InlineAlertProps } from './InlineAlert';
7
- import { lorem40 } from '../test-utils';
8
8
 
9
9
  export default {
10
10
  component: InlineAlert,
@@ -29,17 +29,81 @@ export const Basic = () => {
29
29
 
30
30
  const message = text('message', 'Please enter a password over 5 characters');
31
31
 
32
+ let typeClass = '';
33
+ switch (type) {
34
+ case Sentiment.ERROR:
35
+ case Sentiment.NEGATIVE:
36
+ typeClass = 'has-error';
37
+ break;
38
+ case Sentiment.SUCCESS:
39
+ case Sentiment.POSITIVE:
40
+ typeClass = 'has-success';
41
+ break;
42
+ case Sentiment.INFO:
43
+ case Sentiment.NEUTRAL:
44
+ typeClass = 'has-info';
45
+ break;
46
+ case Sentiment.WARNING:
47
+ typeClass = 'has-warning';
48
+ break;
49
+ case Sentiment.PENDING:
50
+ }
51
+
32
52
  return (
33
53
  <>
54
+ {/* eslint-disable-next-line react/no-adjacent-inline-elements */}
55
+ <p>
56
+ The styling for the input (the coloured border) and the visibility of the inline alert is
57
+ controlled through the use of <code>has-***</code> classes which are applied to the{' '}
58
+ <code>form-group</code> element. For example, to display an inline alert of type error, you
59
+ must also apply the class <code>has-error</code> to the parent <code>form-group</code>{' '}
60
+ element. The available classes are <code>has-error</code>, <code>has-info</code>,{' '}
61
+ <code>has-warning</code> and <code>has-success</code>.
62
+ </p>
34
63
  <p>
35
- Avoid using <code>InlineAlert</code> directly (unless for some custom use cases), for form
36
- control validation, info messaging please use <code>Field</code> component
37
- <pre>{'<Field sentiment={..} message={..}>'}</pre>
64
+ Where possible consumers should use{' '}
65
+ <a href="https://storybook.wise.design/?path=/story/field--basic">Field</a> instead of doing
66
+ this manually.
38
67
  </p>
39
- <InlineAlert type={type}>{message}</InlineAlert>
40
- <InlineAlert type="negative">{message}</InlineAlert>
41
- <InlineAlert type="positive">{lorem40}</InlineAlert>
42
- <InlineAlert type="negative">{message}</InlineAlert>
68
+ <div className={`form-group ${typeClass}`}>
69
+ <label className="control-label" htmlFor="id0">
70
+ Toggleable
71
+ </label>
72
+ <Input id="id0" value="Neptune is cool" />
73
+ <InlineAlert type={type}>{message}</InlineAlert>
74
+ </div>
75
+ <div className="form-group has-error">
76
+ <label className="control-label" htmlFor="id1">
77
+ Negative
78
+ </label>
79
+ <Input id="id1" value="Neptune is cool" />
80
+ <InlineAlert type="negative">{message}</InlineAlert>
81
+ </div>
82
+ <div className="form-group has-success">
83
+ <label className="control-label" htmlFor="id2">
84
+ Positive
85
+ </label>
86
+ <Input id="id2" value="Neptune is cool" />
87
+ <InlineAlert type="positive">
88
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
89
+ ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
90
+ ullamco laboris nisi ut aliquip ex ea commodo consequat.
91
+ </InlineAlert>
92
+ </div>
93
+ <div className="form-group has-neutral">
94
+ <label className="control-label" htmlFor="id3">
95
+ Neutral
96
+ </label>
97
+ <Input id="id3" value="Neptune is cool" />
98
+ <InlineAlert type="neutral">{message}</InlineAlert>
99
+ </div>
100
+ <div className="form-group">
101
+ <label className="control-label" htmlFor="id4">
102
+ No has-* class
103
+ </label>
104
+ <Input id="id4" value="Neptune is cool" />
105
+ <InlineAlert type="negative">{message}</InlineAlert>
106
+ </div>
43
107
  </>
44
108
  );
45
109
  };
@@ -3,7 +3,6 @@ import { ReactNode } from 'react';
3
3
 
4
4
  import { Sentiment, Size } from '../common';
5
5
  import StatusIcon from '../statusIcon';
6
- import Body from '../body';
7
6
 
8
7
  export interface InlineAlertProps {
9
8
  id?: string;
@@ -27,18 +26,17 @@ export default function InlineAlert({
27
26
  children,
28
27
  }: InlineAlertProps) {
29
28
  return (
30
- <Body
29
+ <div
31
30
  role="alert"
32
31
  id={id}
33
32
  className={clsx(
34
33
  'alert alert-detach',
35
34
  `alert-${type === Sentiment.NEGATIVE || type === Sentiment.ERROR ? 'danger' : type}`,
36
- 'd-flex',
37
35
  className,
38
36
  )}
39
37
  >
40
38
  {iconTypes.has(type) && <StatusIcon sentiment={type} size={Size.SMALL} />}
41
- {children}
42
- </Body>
39
+ <div className="np-text-body-default">{children}</div>
40
+ </div>
43
41
  );
44
42
  }
@@ -26,6 +26,6 @@ describe('InputGroup', () => {
26
26
  </InputGroup>
27
27
  </Field>,
28
28
  );
29
- expect(screen.getByLabelText(/Search…/)).toHaveRole('textbox');
29
+ expect(screen.getByLabelText('Search…')).toHaveRole('textbox');
30
30
  });
31
31
  });