@innovaccer/design-system 2.5.0-2 → 2.5.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 (123) hide show
  1. package/.all-contributorsrc +170 -0
  2. package/.github/workflows/jira.yml +1 -2
  3. package/.github/workflows/main.yml +1 -6
  4. package/.github/workflows/test.yml +22 -0
  5. package/CHANGELOG.md +21 -54
  6. package/CONTRIBUTING.md +23 -0
  7. package/README.md +124 -75
  8. package/core/components/atoms/button/Button.tsx +56 -55
  9. package/core/components/atoms/button/__tests__/Button.test.tsx +3 -12
  10. package/core/components/atoms/checkbox/Checkbox.tsx +3 -6
  11. package/core/components/atoms/collapsible/__stories__/index.story.tsx +2 -2
  12. package/core/components/atoms/dropdown/DropdownList.tsx +1 -1
  13. package/core/components/atoms/dropdown/__stories__/Options.tsx +15 -0
  14. package/core/components/atoms/dropdown/__tests__/Dropdown.test.tsx +202 -1
  15. package/core/components/atoms/dropdown/__tests__/Option.test.tsx +3 -0
  16. package/core/components/atoms/message/__stories__/CustomDescription.tsx +25 -0
  17. package/core/components/atoms/metaList/Meta.tsx +3 -1
  18. package/core/components/atoms/metaList/__tests__/MetaList.test.tsx +30 -36
  19. package/core/components/atoms/metricInput/MetricInput.tsx +2 -2
  20. package/core/components/atoms/outsideClick/__stories__/index.story.tsx +1 -1
  21. package/core/components/atoms/radio/Radio.tsx +7 -10
  22. package/core/components/atoms/radio/__tests__/Radio.test.tsx +13 -7
  23. package/core/components/css-utilities/Align/Align.story.tsx +1 -1
  24. package/core/components/css-utilities/Background/Background.story.tsx +1 -1
  25. package/core/components/css-utilities/Border/Border.story.tsx +128 -0
  26. package/core/components/css-utilities/Display/Display.story.tsx +1 -1
  27. package/core/components/css-utilities/Flex/Flex.story.tsx +1 -1
  28. package/core/components/css-utilities/Miscellaneous/Miscellaneous.story.tsx +1 -1
  29. package/core/components/css-utilities/Overflow/Overflow.story.tsx +1 -1
  30. package/core/components/css-utilities/Position/Position.story.tsx +1 -1
  31. package/core/components/css-utilities/Sizing/Sizing.story.tsx +1 -1
  32. package/core/components/css-utilities/Spacing/Spacing.story.tsx +1 -1
  33. package/core/components/molecules/chatMessage/__tests__/ChatMessage.test.tsx +20 -46
  34. package/core/components/molecules/dropzone/__tests__/Dropzone.test.tsx +47 -111
  35. package/core/components/molecules/dropzone/__tests__/Utilities.test.tsx +13 -13
  36. package/core/components/molecules/editableChipInput/EditableChipInput.tsx +3 -1
  37. package/core/components/molecules/editableInput/EditableInput.tsx +5 -3
  38. package/core/components/molecules/editableInput/__stories__/variants/Uncontrolled.story.tsx +1 -1
  39. package/core/components/molecules/editableInput/__tests__/EditableInput.test.tsx +1 -3
  40. package/core/components/molecules/emptyState/_tests_/EmptyState.test.tsx +3 -7
  41. package/core/components/molecules/fileUploader/FileUploaderItem.tsx +13 -2
  42. package/core/components/molecules/fileUploader/__stories__/index.story.tsx +156 -21
  43. package/core/components/molecules/fileUploader/__tests__/FileUploader.test.tsx +21 -80
  44. package/core/components/molecules/fileUploader/__tests__/FileUploaderList.test.tsx +9 -40
  45. package/core/components/molecules/fullscreenModal/FullscreenModal.tsx +7 -13
  46. package/core/components/molecules/fullscreenModal/__stories__/Layering.story.tsx +2 -2
  47. package/core/components/molecules/modal/Modal.tsx +18 -17
  48. package/core/components/molecules/modal/ModalBody.tsx +1 -1
  49. package/core/components/molecules/modal/__stories__/Layering.story.tsx +1 -1
  50. package/core/components/molecules/modal/__stories__/NoFooter.story.tsx +0 -1
  51. package/core/components/molecules/modal/__stories__/Scrolling.story.tsx +20 -38
  52. package/core/components/molecules/modal/__tests__/Modal.test.tsx +1 -1
  53. package/core/components/molecules/sidesheet/Sidesheet.tsx +16 -17
  54. package/core/components/organisms/choiceList/ChoiceList.tsx +212 -0
  55. package/core/components/organisms/choiceList/__stories__/Alignment.story.tsx +32 -0
  56. package/core/components/organisms/choiceList/__stories__/AllowMultiple.story.tsx +23 -0
  57. package/core/components/organisms/choiceList/__stories__/Controlled.story.tsx +34 -0
  58. package/core/components/organisms/choiceList/__stories__/index.story.tsx +18 -0
  59. package/core/components/organisms/choiceList/__tests__/ChoiceList.test.tsx +155 -0
  60. package/core/components/organisms/choiceList/__tests__/__snapshots__/ChoiceList.test.tsx.snap +3393 -0
  61. package/core/components/organisms/choiceList/index.tsx +2 -0
  62. package/core/components/organisms/datePicker/__tests__/DatePicker.test.tsx +136 -46
  63. package/core/components/organisms/datePicker/__tests__/__snapshots__/DatePicker.test.tsx.snap +2594 -102
  64. package/core/components/organisms/dateRangePicker/DateRangePicker.tsx +5 -0
  65. package/core/components/organisms/dateRangePicker/__tests__/DateRangePicker.test.tsx +49 -410
  66. package/core/components/organisms/dateRangePicker/__tests__/Utilities.test.tsx +39 -0
  67. package/core/components/organisms/dateRangePicker/__tests__/__snapshots__/DateRangePicker.test.tsx.snap +45390 -2679
  68. package/core/components/organisms/dateRangePicker/utilities.tsx +2 -5
  69. package/core/components/organisms/grid/Cell.tsx +5 -4
  70. package/core/components/organisms/grid/Grid.tsx +1 -1
  71. package/core/components/organisms/grid/GridCell.tsx +18 -7
  72. package/core/components/organisms/grid/GridHead.tsx +1 -1
  73. package/core/components/organisms/grid/GridRow.tsx +5 -12
  74. package/core/components/organisms/grid/__tests__/Grid.test.tsx +179 -1
  75. package/core/components/organisms/grid/__tests__/GridCell.test.tsx +218 -0
  76. package/core/components/organisms/grid/__tests__/__snapshots__/Grid.test.tsx.snap +1024 -0
  77. package/core/components/organisms/grid/__tests__/rowUtility.test.tsx +62 -0
  78. package/core/components/organisms/inlineMessage/InlineMessage.tsx +10 -14
  79. package/core/components/organisms/inlineMessage/__stories__/InlineMessageWithinTable.story.tsx +9 -12
  80. package/core/components/organisms/inlineMessage/__stories__/variants/Default.story.tsx +2 -4
  81. package/core/components/organisms/inlineMessage/__stories__/variants/Error.story.tsx +2 -5
  82. package/core/components/organisms/inlineMessage/__stories__/variants/Info.story.tsx +2 -5
  83. package/core/components/organisms/inlineMessage/__stories__/variants/Success.story.tsx +2 -5
  84. package/core/components/organisms/inlineMessage/__stories__/variants/Warning.story.tsx +2 -5
  85. package/core/components/organisms/inlineMessage/__tests__/InlineMessage.test.tsx +4 -20
  86. package/core/components/organisms/navigation/VerticalNavigation.tsx +14 -3
  87. package/core/components/organisms/navigation/__tests__/Navigation.test.tsx +179 -0
  88. package/core/components/organisms/navigation/__tests__/__snapshots__/Navigation.test.tsx.snap +530 -0
  89. package/core/components/organisms/table/DraggableDropdown.tsx +1 -0
  90. package/core/components/organisms/table/Header.tsx +11 -2
  91. package/core/components/organisms/table/Table.tsx +2 -2
  92. package/core/components/organisms/table/__stories__/NestedTableWithNestedCard.story.tsx +4 -1
  93. package/core/components/organisms/table/__stories__/variants/nestedRows.story.tsx +5 -2
  94. package/core/components/organisms/table/__tests__/Table.test.tsx +292 -0
  95. package/core/components/organisms/table/__tests__/__snapshots__/Table.test.tsx.snap +349041 -0
  96. package/core/components/organisms/timePicker/__tests__/TimePicker.test.tsx +15 -66
  97. package/core/components/patterns/dateRangePicker/withCustomPopover.story.tsx +47 -36
  98. package/core/index.tsx +1 -1
  99. package/core/index.type.tsx +1 -0
  100. package/core/utils/OverlayManager.tsx +1 -3
  101. package/core/utils/__tests__/__snapshots__/TS.test.tsx.snap +4 -0
  102. package/core/utils/types.tsx +3 -4
  103. package/css/dist/index.css +78 -8
  104. package/css/dist/index.css.map +1 -1
  105. package/css/src/components/button.css +8 -4
  106. package/css/src/components/choiceList.css +25 -0
  107. package/css/src/components/modal.css +1 -2
  108. package/css/src/utils/border.css +39 -0
  109. package/dist/core/components/atoms/popperWrapper/PopperWrapper.d.ts +50 -49
  110. package/dist/core/components/css-utilities/Border/Border.story.d.ts +13 -0
  111. package/dist/core/components/organisms/choiceList/ChoiceList.d.ts +33 -0
  112. package/dist/core/components/organisms/choiceList/index.d.ts +2 -0
  113. package/dist/core/components/organisms/dateRangePicker/utilities.d.ts +2 -2
  114. package/dist/core/index.d.ts +1 -0
  115. package/dist/core/index.type.d.ts +1 -0
  116. package/dist/index.esm.js +207 -58
  117. package/dist/index.js +209 -57
  118. package/dist/index.js.map +1 -1
  119. package/dist/index.umd.js +1 -1
  120. package/dist/index.umd.js.br +0 -0
  121. package/dist/index.umd.js.gz +0 -0
  122. package/jest.config.js +2 -1
  123. package/package.json +6 -4
@@ -144,21 +144,21 @@ class Modal extends React.Component<ModalProps, ModalState> {
144
144
 
145
145
  onCloseHandler = (event: KeyboardEvent) => {
146
146
  const isTopOverlay = OverlayManager.isTopOverlay(this.modalRef.current);
147
- closeOnEscapeKeypress(event, isTopOverlay, this.onOutsideClickHandler)
148
- }
147
+ closeOnEscapeKeypress(event, isTopOverlay, this.onOutsideClickHandler);
148
+ };
149
149
 
150
150
  componentDidMount() {
151
151
  if (this.props.closeOnEscape) {
152
152
  if (this.state.open) {
153
153
  OverlayManager.add(this.modalRef.current);
154
154
  }
155
- document.addEventListener('keydown', this.onCloseHandler)
155
+ document.addEventListener('keydown', this.onCloseHandler);
156
156
  }
157
157
  }
158
158
 
159
159
  componentWillUnmount() {
160
160
  if (this.props.closeOnEscape) {
161
- document.removeEventListener('keydown', this.onCloseHandler)
161
+ document.removeEventListener('keydown', this.onCloseHandler);
162
162
  }
163
163
  }
164
164
 
@@ -178,20 +178,21 @@ class Modal extends React.Component<ModalProps, ModalState> {
178
178
  });
179
179
 
180
180
  if (this.props.closeOnEscape) OverlayManager.add(this.modalRef.current);
181
-
182
181
  } else {
183
- this.setState({
184
- animate: false,
185
- }, () => {
186
- window.setTimeout(() => {
187
- this.setState({
188
- open: false
189
- });
190
- }, 120);
191
- });
182
+ this.setState(
183
+ {
184
+ animate: false,
185
+ },
186
+ () => {
187
+ window.setTimeout(() => {
188
+ this.setState({
189
+ open: false,
190
+ });
191
+ }, 120);
192
+ }
193
+ );
192
194
 
193
195
  if (this.props.closeOnEscape) OverlayManager.remove(this.modalRef.current);
194
-
195
196
  }
196
197
  }
197
198
  }
@@ -251,12 +252,12 @@ class Modal extends React.Component<ModalProps, ModalState> {
251
252
  ['Overlay-container--open']: open,
252
253
  });
253
254
 
254
- const isAPINew = (headerOptions || footerOptions || footer || header);
255
+ const isAPINew = headerOptions || footerOptions || footer || header;
255
256
  const bodyClass = classNames({
256
257
  ['Modal-body']: true,
257
258
  ['Modal-body--withMargin']: isAPINew ? !!footer : true,
258
259
  ['Modal-body--withPadding']: isAPINew ? !footer : true,
259
- })
260
+ });
260
261
 
261
262
  const baseProps = extractBaseProps(this.props);
262
263
  const sizeMap: Record<ModalProps['dimension'], Partial<ColumnProps>> = {
@@ -17,7 +17,7 @@ export const ModalBody = (props: ModalBodyProps) => {
17
17
  const classes = classNames(
18
18
  {
19
19
  'Modal-body': true,
20
- 'Modal-body--withMargin': true
20
+ 'Modal-body--withMargin': true,
21
21
  },
22
22
  className
23
23
  );
@@ -18,7 +18,7 @@ export const layering = () => {
18
18
  const onCloseSecondOverlay = () => {
19
19
  updateKnob('openSecondOverlay', false);
20
20
  action('on close triggered')();
21
- }
21
+ };
22
22
 
23
23
  return (
24
24
  <div>
@@ -35,7 +35,6 @@ export const noFooter = () => {
35
35
  heading: 'Heading',
36
36
  subHeading: 'Subheading',
37
37
  }}
38
-
39
38
  >
40
39
  <Text>Modal Body</Text>
41
40
  <ModalDescription
@@ -31,50 +31,36 @@ export const scrolling = () => {
31
31
  },
32
32
  {
33
33
  question:
34
- 'Moving or speaking so slowly that other people could have noticed? Or the opposite - being so fidgety or restless that you have been moving around a lot more than usual',
34
+ 'Moving or speaking so slowly that other people could have noticed? Or the opposite - being so fidgety or restless that you have been moving around a lot more than usual',
35
35
  options: ['Not at all', 'Several Days', 'More than half the days', 'Nearly every day'],
36
36
  },
37
37
  {
38
- question: 'Feeling tired or having little energy?', options: ['Yes', 'No']
38
+ question: 'Feeling tired or having little energy?',
39
+ options: ['Yes', 'No'],
39
40
  },
40
41
  {
41
- question: 'Which of the following refers to a programme that aims to enable patients to make better use of information and communication technology for health and health care?',
42
- options: [
43
- 'Patient informatics',
44
- 'ICT health',
45
- 'Health-tech',
46
- 'None of these',
47
- ],
42
+ question:
43
+ 'Which of the following refers to a programme that aims to enable patients to make better use of information and communication technology for health and health care?',
44
+ options: ['Patient informatics', 'ICT health', 'Health-tech', 'None of these'],
48
45
  },
49
46
  {
50
- question: 'The way messages are framed influences people’s intentions and willingness to change their behaviour. Which of the following refers to the type of message framing that gives information about a health behaviour that emphasizes the costs of failing to take action?',
51
- options: [
52
- 'Gain-framed messages',
53
- 'Loss-framed messages',
54
- 'Neutrally-framed messages',
55
- 'None of these',
56
- ],
47
+ question:
48
+ 'The way messages are framed influences people’s intentions and willingness to change their behaviour. Which of the following refers to the type of message framing that gives information about a health behaviour that emphasizes the costs of failing to take action?',
49
+ options: ['Gain-framed messages', 'Loss-framed messages', 'Neutrally-framed messages', 'None of these'],
57
50
  },
58
51
  {
59
- question: 'Which of the following refers to the capacity to access, understand, appraise and apply health information and services, and to make appropriate health decisions to promote and maintain health?',
60
- options: [
61
- 'health accessibility',
62
- 'health appraisal',
63
- 'health literacy',
64
- 'health promotion',
65
- ],
52
+ question:
53
+ 'Which of the following refers to the capacity to access, understand, appraise and apply health information and services, and to make appropriate health decisions to promote and maintain health?',
54
+ options: ['health accessibility', 'health appraisal', 'health literacy', 'health promotion'],
66
55
  },
67
56
  {
68
- question: 'Frederich Engels’ book entitled The Condition of the Working Class in England in 1844 provided a detailed description of the appalling living and working conditions and the limited health care of working-class residents in which of the following English cities?',
69
- options: [
70
- 'London',
71
- 'Manchester',
72
- 'Liverpool',
73
- 'None of these',
74
- ],
57
+ question:
58
+ 'Frederich Engels’ book entitled The Condition of the Working Class in England in 1844 provided a detailed description of the appalling living and working conditions and the limited health care of working-class residents in which of the following English cities?',
59
+ options: ['London', 'Manchester', 'Liverpool', 'None of these'],
75
60
  },
76
61
  {
77
- question: 'Which of the following explanations for health inequalities focus on the individual as the unit of analysis, emphasizing unthinking, reckless or irresponsible behaviour or incautious lifestyle as the moving determinant?',
62
+ question:
63
+ 'Which of the following explanations for health inequalities focus on the individual as the unit of analysis, emphasizing unthinking, reckless or irresponsible behaviour or incautious lifestyle as the moving determinant?',
78
64
  options: [
79
65
  'Individualist explanations',
80
66
  'Natural and social selection',
@@ -83,13 +69,9 @@ export const scrolling = () => {
83
69
  ],
84
70
  },
85
71
  {
86
- question: 'This approach to health promotion is based on the assumption that humans are rational decision-makers, this approach relies heavily upon the provision of information about risks and benefits of certain behaviours.',
87
- options: [
88
- 'Behaviour change approach',
89
- 'Community development approach',
90
- 'Biomedical approach',
91
- 'None of these',
92
- ],
72
+ question:
73
+ 'This approach to health promotion is based on the assumption that humans are rational decision-makers, this approach relies heavily upon the provision of information about risks and benefits of certain behaviours.',
74
+ options: ['Behaviour change approach', 'Community development approach', 'Biomedical approach', 'None of these'],
93
75
  },
94
76
  ];
95
77
 
@@ -169,7 +169,7 @@ describe('Modal component with props', () => {
169
169
  });
170
170
 
171
171
  it('renders children without footer props', () => {
172
- const { getByTestId, queryByTestId } = render(<Modal backdropClose={FunctionValue} open={true} header={header}/>);
172
+ const { getByTestId, queryByTestId } = render(<Modal backdropClose={FunctionValue} open={true} header={header} />);
173
173
  expect(getByTestId('DesignSystem-ModalContainer')).toBeInTheDocument();
174
174
  expect(getByTestId('DesignSystem-Modal')).toBeInTheDocument();
175
175
  expect(queryByTestId('Modal-body--withMargin')).not.toBeInTheDocument();
@@ -138,27 +138,26 @@ class Sidesheet extends React.Component<SidesheetProps, SidesheetState> {
138
138
 
139
139
  onCloseHandler = (event: KeyboardEvent) => {
140
140
  const isTopOverlay = OverlayManager.isTopOverlay(this.sidesheetRef.current);
141
- closeOnEscapeKeypress(event, isTopOverlay, this.onOutsideClickHandler)
142
- }
141
+ closeOnEscapeKeypress(event, isTopOverlay, this.onOutsideClickHandler);
142
+ };
143
143
 
144
144
  componentDidMount() {
145
145
  if (this.props.closeOnEscape) {
146
146
  if (this.state.open) {
147
147
  OverlayManager.add(this.sidesheetRef.current);
148
148
  }
149
- document.addEventListener('keydown', this.onCloseHandler)
149
+ document.addEventListener('keydown', this.onCloseHandler);
150
150
  }
151
151
  }
152
152
 
153
153
  componentWillUnmount() {
154
154
  if (this.props.closeOnEscape) {
155
- document.removeEventListener('keydown', this.onCloseHandler)
155
+ document.removeEventListener('keydown', this.onCloseHandler);
156
156
  }
157
157
  }
158
158
 
159
159
  componentDidUpdate(prevProps: SidesheetProps) {
160
160
  if (prevProps.open !== this.props.open) {
161
-
162
161
  if (this.props.open) {
163
162
  const zIndex = getUpdatedZIndex({
164
163
  element: this.element,
@@ -173,21 +172,21 @@ class Sidesheet extends React.Component<SidesheetProps, SidesheetState> {
173
172
  });
174
173
 
175
174
  if (this.props.closeOnEscape) OverlayManager.add(this.sidesheetRef.current);
176
-
177
175
  } else {
178
-
179
- this.setState({
180
- animate: false,
181
- }, () => {
182
- window.setTimeout(() => {
183
- this.setState({
184
- open: false
185
- });
186
- }, 120);
187
- });
176
+ this.setState(
177
+ {
178
+ animate: false,
179
+ },
180
+ () => {
181
+ window.setTimeout(() => {
182
+ this.setState({
183
+ open: false,
184
+ });
185
+ }, 120);
186
+ }
187
+ );
188
188
 
189
189
  if (this.props.closeOnEscape) OverlayManager.remove(this.sidesheetRef.current);
190
-
191
190
  }
192
191
  }
193
192
  }
@@ -0,0 +1,212 @@
1
+ import * as React from 'react';
2
+ import classNames from 'classnames';
3
+ import { Checkbox, Radio, Label } from '@/index';
4
+ import { BaseProps } from '@/utils/types';
5
+
6
+ export type Alignment = 'horizontal' | 'vertical';
7
+ export type Size = 'regular' | 'tiny';
8
+ type ChangeEvent = React.ChangeEvent<HTMLInputElement>;
9
+ type noop = (ev: ChangeEvent) => void;
10
+
11
+ export interface Choice {
12
+ /**
13
+ * Value of the choice
14
+ */
15
+ value: string;
16
+ /**
17
+ * Label for the choice
18
+ */
19
+ label?: string;
20
+ /**
21
+ * Disable choice
22
+ */
23
+ disabled?: boolean;
24
+ /**
25
+ * Additional text
26
+ */
27
+ helpText?: string;
28
+ /**
29
+ * Adds name to the choice
30
+ */
31
+ name: string;
32
+ }
33
+ export interface ChoiceListProps extends BaseProps {
34
+ /**
35
+ * Describes title of the `ChoiceList`
36
+ */
37
+ title?: string;
38
+ /**
39
+ * Describes Collection of choices
40
+ */
41
+ choices: Choice[];
42
+ /**
43
+ * Alignment in which the coices will be rendered
44
+ * @default "vertical"
45
+ */
46
+ alignment?: Alignment;
47
+ /**
48
+ * Size of the `ChoiceList`
49
+ * @default "regular"
50
+ */
51
+ size?: Size;
52
+ /**
53
+ * renders `checkbox` if `true` and renders `radio` if `false` of the `ChoiceList`
54
+ * @default false
55
+ */
56
+ allowMultiple?: boolean;
57
+ /**
58
+ * Disable the `ChoiceList`
59
+ * @default false
60
+ */
61
+ disabled?: boolean;
62
+ /**
63
+ * Collection of selected choices
64
+ */
65
+ selected?: string[];
66
+ /**
67
+ * Callback when the selected choices change
68
+ */
69
+ onChange?(event: ChangeEvent, selected: string[]): void;
70
+ }
71
+
72
+ const renderCheckbox = (
73
+ list: Choice[],
74
+ handleOnChange: noop,
75
+ ChoiceListDisabled: boolean,
76
+ size: Size,
77
+ alignment: Alignment,
78
+ selected: string[]
79
+ ) => {
80
+ return list.map((item: Choice, checkboxIndex: number) => {
81
+ const { name, value, helpText, disabled, label } = item;
82
+ return (
83
+ <Checkbox
84
+ key={checkboxIndex}
85
+ label={label}
86
+ onChange={handleOnChange}
87
+ disabled={disabled || ChoiceListDisabled}
88
+ helpText={helpText}
89
+ size={size}
90
+ name={name}
91
+ value={value}
92
+ defaultChecked={selected.length !== 0 && selected.includes(value)}
93
+ className={getCheckboxClassName(alignment, checkboxIndex)}
94
+ />
95
+ );
96
+ });
97
+ };
98
+
99
+ const renderRadio = (
100
+ list: Choice[],
101
+ handleOnChange: noop,
102
+ ChoiceListDisabled: boolean,
103
+ size: Size,
104
+ alignment: Alignment,
105
+ selected: string[]
106
+ ) => {
107
+ return list.map((item: Choice, radioIndex: number) => {
108
+ const { name, value, helpText, disabled, label } = item;
109
+ return (
110
+ <Radio
111
+ key={radioIndex}
112
+ label={label}
113
+ onChange={handleOnChange}
114
+ disabled={disabled || ChoiceListDisabled}
115
+ helpText={helpText}
116
+ size={size}
117
+ name={name}
118
+ value={value}
119
+ defaultChecked={selected.length !== 0 && selected.includes(value)}
120
+ className={getRadioClassName(alignment, radioIndex)}
121
+ />
122
+ );
123
+ });
124
+ };
125
+
126
+ const getCheckboxClassName = (alignment: Alignment, index: number) => {
127
+ const ChoiceListCheckboxClass = classNames({
128
+ [`ChoiceList-checkbox--${alignment}`]: true,
129
+ ['ml-0']: index === 0 && alignment === 'horizontal',
130
+ ['mt-0']: index === 0 && alignment === 'vertical',
131
+ });
132
+ return ChoiceListCheckboxClass;
133
+ };
134
+
135
+ const getRadioClassName = (alignment: Alignment, index: number) => {
136
+ const ChoiceListRadioClass = classNames({
137
+ [`ChoiceList-radio--${alignment}`]: true,
138
+ ['ml-0']: index === 0 && alignment === 'horizontal',
139
+ ['mt-0']: index === 0 && alignment === 'vertical',
140
+ });
141
+ return ChoiceListRadioClass;
142
+ };
143
+
144
+ export const ChoiceList = (props: ChoiceListProps) => {
145
+ const {
146
+ title,
147
+ choices,
148
+ alignment = 'vertical',
149
+ allowMultiple = false,
150
+ onChange,
151
+ disabled = false,
152
+ size = 'regular',
153
+ } = props;
154
+
155
+ const { selected = [] } = props;
156
+ let selectedChoiceValue = (selected && selected) || [];
157
+ const ChoiceListClass = classNames({
158
+ ['ChoiceList']: true,
159
+ });
160
+
161
+ const ChoiceListVerticalClass = classNames({
162
+ ['ChoiceList--alignVertical']: true,
163
+ });
164
+
165
+ const ChoiceHorizontalClass = classNames({
166
+ ['ChoiceList--alignHorizontal']: true,
167
+ });
168
+
169
+ const handleOnChange = (e: React.ChangeEvent<HTMLInputElement>) => {
170
+ if (e.target.checked && allowMultiple) {
171
+ if (!selectedChoiceValue.includes(e.target.value)) {
172
+ selectedChoiceValue = [...selectedChoiceValue, e.target.value];
173
+ }
174
+ } else if (!e.target.checked && allowMultiple) {
175
+ selectedChoiceValue = selectedChoiceValue.filter((el) => el !== e.target.value);
176
+ }
177
+ if (!allowMultiple) {
178
+ if (!selectedChoiceValue.includes(e.target.value)) {
179
+ selectedChoiceValue = [];
180
+ selectedChoiceValue = [...selectedChoiceValue, e.target.value];
181
+ }
182
+ }
183
+ if (onChange) onChange(e, selectedChoiceValue);
184
+ };
185
+
186
+ return (
187
+ <>
188
+ <fieldset className={ChoiceListClass} data-test="DesignSystem-ChoiceList-Wrapper">
189
+ {title && title.trim() && <Label>{title.trim()}</Label>}
190
+ {!!allowMultiple ? (
191
+ <div className={`${alignment === 'horizontal' ? ChoiceHorizontalClass : ChoiceListVerticalClass}`}>
192
+ {renderCheckbox(choices, handleOnChange, disabled, size, alignment, selected)}
193
+ </div>
194
+ ) : (
195
+ <div className={`${alignment === 'horizontal' ? ChoiceHorizontalClass : ChoiceListVerticalClass}`}>
196
+ {renderRadio(choices, handleOnChange, disabled, size, alignment, selected)}
197
+ </div>
198
+ )}
199
+ </fieldset>
200
+ </>
201
+ );
202
+ };
203
+
204
+ ChoiceList.displayName = 'ChoiceList';
205
+ ChoiceList.defaultProps = {
206
+ alignment: 'vertical',
207
+ size: 'regular',
208
+ allowMultiple: false,
209
+ disabled: false,
210
+ };
211
+
212
+ export default ChoiceList;
@@ -0,0 +1,32 @@
1
+ import * as React from 'react';
2
+ import ChoiceList from '../ChoiceList';
3
+
4
+ export const alignment = () => {
5
+ const label = 'Gender';
6
+ const alignmentHorizontal = 'horizontal';
7
+ const gender = [
8
+ { label: 'Male', name: 'gender', value: 'Male' },
9
+ { label: 'Female', name: 'gender', value: 'Female' },
10
+ { label: 'Other', name: 'gender', value: 'Other' },
11
+ ];
12
+ const alignmentVertical = 'vertical';
13
+ const allGender = [
14
+ { label: 'Male', name: 'allGender', value: 'Male' },
15
+ { label: 'Female', name: 'allGender', value: 'Female' },
16
+ { label: 'Other', name: 'allGender', value: 'Other' },
17
+ ];
18
+
19
+ return (
20
+ <>
21
+ <ChoiceList choices={gender} title={label} alignment={alignmentHorizontal} onChange={() => {}} />
22
+ <div className="mt-8">
23
+ <ChoiceList choices={allGender} title={label} alignment={alignmentVertical} onChange={() => {}} />
24
+ </div>
25
+ </>
26
+ );
27
+ };
28
+
29
+ export default {
30
+ title: 'Components/ChoiceList/Alignment',
31
+ component: ChoiceList,
32
+ };
@@ -0,0 +1,23 @@
1
+ import * as React from 'react';
2
+ import ChoiceList from '../ChoiceList';
3
+
4
+ export const allowMultiple = () => {
5
+ const label = 'Days';
6
+ const alignment = 'horizontal';
7
+ const days = [
8
+ { label: 'Mon', name: 'days', value: 'mon' },
9
+ { label: 'Tue', name: 'days', value: 'tue' },
10
+ { label: 'Wed', name: 'days', value: 'wed' },
11
+ { label: 'Thu', name: 'days', value: 'thu' },
12
+ { label: 'Fri', name: 'days', value: 'fri' },
13
+ { label: 'Sat', name: 'days', value: 'sat' },
14
+ { label: 'Sun', name: 'days', value: 'sun' },
15
+ ];
16
+
17
+ return <ChoiceList choices={days} title={label} alignment={alignment} allowMultiple={true} onChange={() => {}} />;
18
+ };
19
+
20
+ export default {
21
+ title: 'Components/ChoiceList/Allow Multiple',
22
+ component: ChoiceList,
23
+ };
@@ -0,0 +1,34 @@
1
+ import * as React from 'react';
2
+ import ChoiceList from '../ChoiceList';
3
+
4
+ export const Controlled = () => {
5
+ const label = 'Gender';
6
+ const alignmentHorizontal = 'horizontal';
7
+ const days = [
8
+ { label: 'Mon', name: 'days', value: 'mon' },
9
+ { label: 'Tue', name: 'days', value: 'tue' },
10
+ { label: 'Wed', name: 'days', value: 'wed' },
11
+ { label: 'Thu', name: 'days', value: 'thu' },
12
+ { label: 'Fri', name: 'days', value: 'fri' },
13
+ { label: 'Sat', name: 'days', value: 'sat' },
14
+ { label: 'Sun', name: 'days', value: 'sun' },
15
+ ];
16
+
17
+ return (
18
+ <>
19
+ <ChoiceList
20
+ selected={['mon', 'wed', 'sat']}
21
+ choices={days}
22
+ allowMultiple={true}
23
+ title={label}
24
+ alignment={alignmentHorizontal}
25
+ onChange={() => {}}
26
+ />
27
+ </>
28
+ );
29
+ };
30
+
31
+ export default {
32
+ title: 'Components/ChoiceList/Controlled',
33
+ component: ChoiceList,
34
+ };
@@ -0,0 +1,18 @@
1
+ import * as React from 'react';
2
+ import ChoiceList from '../ChoiceList';
3
+
4
+ export const all = () => {
5
+ const label = 'Gender';
6
+ const gender = [
7
+ { label: 'Male', name: 'gender', value: 'Male' },
8
+ { label: 'Female', name: 'gender', value: 'Female' },
9
+ { label: 'Other', name: 'gender', value: 'Other' },
10
+ ];
11
+
12
+ return <ChoiceList choices={gender} title={label} onChange={() => {}} />;
13
+ };
14
+
15
+ export default {
16
+ title: 'Components/ChoiceList/All',
17
+ component: ChoiceList,
18
+ };