@nypl/design-system-react-components 0.25.4 → 0.25.8

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 (85) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/components/Breadcrumbs/BreadcrumbsTypes.d.ts +1 -0
  3. package/dist/components/Icons/IconSvgs.d.ts +1 -0
  4. package/dist/components/Icons/IconTypes.d.ts +1 -0
  5. package/dist/components/Image/ImageTypes.d.ts +3 -1
  6. package/dist/components/Link/LinkTypes.d.ts +1 -0
  7. package/dist/components/Notification/Notification.d.ts +2 -0
  8. package/dist/components/Select/Select.d.ts +2 -0
  9. package/dist/components/Toggle/Toggle.d.ts +47 -0
  10. package/dist/components/Toggle/ToggleSizes.d.ts +4 -0
  11. package/dist/components/VideoPlayer/VideoPlayer.d.ts +5 -3
  12. package/dist/design-system-react-components.cjs.development.js +921 -360
  13. package/dist/design-system-react-components.cjs.development.js.map +1 -1
  14. package/dist/design-system-react-components.cjs.production.min.js +1 -1
  15. package/dist/design-system-react-components.cjs.production.min.js.map +1 -1
  16. package/dist/design-system-react-components.esm.js +917 -353
  17. package/dist/design-system-react-components.esm.js.map +1 -1
  18. package/dist/index.d.ts +5 -0
  19. package/dist/theme/components/breadcrumb.d.ts +9 -0
  20. package/dist/theme/components/notification.d.ts +8 -4
  21. package/dist/theme/components/toggle.d.ts +68 -0
  22. package/dist/theme/foundations/spacing.d.ts +2 -0
  23. package/package.json +2 -2
  24. package/src/components/Accordion/Accordion.stories.mdx +1 -2
  25. package/src/components/Breadcrumbs/Breadcrumbs.stories.mdx +20 -3
  26. package/src/components/Breadcrumbs/Breadcrumbs.test.tsx +20 -0
  27. package/src/components/Breadcrumbs/BreadcrumbsTypes.tsx +1 -0
  28. package/src/components/Breadcrumbs/__snapshots__/Breadcrumbs.test.tsx.snap +198 -0
  29. package/src/components/Button/Button.tsx +0 -1
  30. package/src/components/Card/Card.stories.mdx +74 -7
  31. package/src/components/Card/Card.tsx +9 -8
  32. package/src/components/Card/__snapshots__/Card.test.tsx.snap +67 -35
  33. package/src/components/Chakra/Grid.stories.mdx +11 -14
  34. package/src/components/DatePicker/DatePicker.test.tsx +8 -6
  35. package/src/components/DatePicker/__snapshots__/DatePicker.test.tsx.snap +5 -4
  36. package/src/components/Icons/Icon.stories.mdx +3 -2
  37. package/src/components/Icons/IconSvgs.tsx +2 -0
  38. package/src/components/Icons/IconTypes.tsx +1 -0
  39. package/src/components/Image/Image.stories.mdx +152 -90
  40. package/src/components/Image/Image.test.tsx +10 -0
  41. package/src/components/Image/ImageTypes.ts +2 -0
  42. package/src/components/Image/__snapshots__/Image.test.tsx.snap +24 -8
  43. package/src/components/Label/Label.stories.mdx +1 -1
  44. package/src/components/Link/Link.stories.mdx +2 -3
  45. package/src/components/Link/Link.test.tsx +71 -0
  46. package/src/components/Link/Link.tsx +41 -9
  47. package/src/components/Link/LinkTypes.tsx +1 -0
  48. package/src/components/Link/__snapshots__/Link.test.tsx.snap +201 -0
  49. package/src/components/Notification/Notification.stories.mdx +36 -3
  50. package/src/components/Notification/Notification.test.tsx +62 -16
  51. package/src/components/Notification/Notification.tsx +17 -5
  52. package/src/components/Notification/__snapshots__/Notification.test.tsx.snap +117 -0
  53. package/src/components/Pagination/Pagination.stories.mdx +1 -2
  54. package/src/components/Pagination/__snapshots__/Pagination.test.tsx.snap +42 -0
  55. package/src/components/ProgressIndicator/ProgressIndicator.stories.mdx +2 -3
  56. package/src/components/SearchBar/SearchBar.Test.tsx +64 -20
  57. package/src/components/SearchBar/SearchBar.stories.mdx +3 -4
  58. package/src/components/SearchBar/SearchBar.tsx +4 -1
  59. package/src/components/Select/Select.stories.mdx +132 -55
  60. package/src/components/Select/Select.test.tsx +2 -2
  61. package/src/components/Select/Select.tsx +6 -2
  62. package/src/components/Slider/Slider.stories.mdx +3 -2
  63. package/src/components/Slider/Slider.test.tsx +35 -0
  64. package/src/components/Slider/Slider.tsx +8 -2
  65. package/src/components/Template/Template.stories.mdx +1 -2
  66. package/src/components/Toggle/Toggle.stories.mdx +176 -0
  67. package/src/components/Toggle/Toggle.test.tsx +140 -0
  68. package/src/components/Toggle/Toggle.tsx +118 -0
  69. package/src/components/Toggle/ToggleSizes.tsx +4 -0
  70. package/src/components/Toggle/__snapshots__/Toggle.test.tsx.snap +255 -0
  71. package/src/components/VideoPlayer/VideoPlayer.stories.mdx +39 -18
  72. package/src/components/VideoPlayer/VideoPlayer.test.tsx +103 -1
  73. package/src/components/VideoPlayer/VideoPlayer.tsx +57 -17
  74. package/src/components/VideoPlayer/__snapshots__/VideoPlayer.test.tsx.snap +48 -0
  75. package/src/index.ts +8 -0
  76. package/src/theme/components/breadcrumb.ts +11 -1
  77. package/src/theme/components/card.ts +4 -5
  78. package/src/theme/components/global.ts +1 -1
  79. package/src/theme/components/icon.ts +2 -2
  80. package/src/theme/components/image.ts +8 -0
  81. package/src/theme/components/notification.ts +8 -6
  82. package/src/theme/components/toggle.ts +65 -0
  83. package/src/theme/foundations/spacing.ts +3 -0
  84. package/src/theme/index.ts +2 -0
  85. package/src/utils/componentCategories.ts +3 -1
@@ -35,13 +35,13 @@ describe("SearchBar Accessibility", () => {
35
35
  it("passes axe accessibility test", async () => {
36
36
  const { container } = render(
37
37
  <SearchBar
38
+ helperErrorText={helperErrorText}
38
39
  id="id"
40
+ invalidText={invalidText}
39
41
  labelText="Searchbar"
40
42
  onSubmit={jest.fn()}
41
43
  selectProps={selectProps}
42
44
  textInputProps={textInputProps}
43
- helperErrorText={helperErrorText}
44
- invalidText={invalidText}
45
45
  />
46
46
  );
47
47
  expect(await axe(container)).toHaveNoViolations();
@@ -55,11 +55,11 @@ describe("SearchBar", () => {
55
55
  it("renders the basic form", () => {
56
56
  render(
57
57
  <SearchBar
58
+ helperErrorText={helperErrorText}
58
59
  id="id"
59
60
  labelText="searchbar"
60
61
  onSubmit={searchBarSubmit}
61
62
  textInputProps={textInputProps}
62
- helperErrorText={helperErrorText}
63
63
  />
64
64
  );
65
65
  expect(screen.getByRole("search")).toBeInTheDocument();
@@ -74,12 +74,12 @@ describe("SearchBar", () => {
74
74
  it("renders an optional Select component", () => {
75
75
  render(
76
76
  <SearchBar
77
+ helperErrorText={helperErrorText}
77
78
  id="id"
78
79
  labelText="searchbar"
79
80
  onSubmit={searchBarSubmit}
80
81
  selectProps={selectProps}
81
82
  textInputProps={textInputProps}
82
- helperErrorText={helperErrorText}
83
83
  />
84
84
  );
85
85
  expect(screen.getByRole("combobox")).toBeInTheDocument();
@@ -89,14 +89,14 @@ describe("SearchBar", () => {
89
89
  it("renders the invalid text in the invalid state", () => {
90
90
  render(
91
91
  <SearchBar
92
+ helperErrorText={helperErrorText}
92
93
  id="id"
94
+ invalidText={invalidText}
95
+ isInvalid
93
96
  labelText="searchbar"
94
97
  onSubmit={searchBarSubmit}
95
98
  selectProps={selectProps}
96
99
  textInputProps={textInputProps}
97
- helperErrorText={helperErrorText}
98
- invalidText={invalidText}
99
- isInvalid
100
100
  />
101
101
  );
102
102
  expect(screen.getByText(invalidText)).toBeInTheDocument();
@@ -106,14 +106,14 @@ describe("SearchBar", () => {
106
106
  it("does not render the default invalid text from the Select or TextInput components", () => {
107
107
  render(
108
108
  <SearchBar
109
+ helperErrorText={helperErrorText}
109
110
  id="id"
111
+ invalidText={invalidText}
112
+ isInvalid
110
113
  labelText="searchbar"
111
114
  onSubmit={searchBarSubmit}
112
115
  selectProps={selectProps}
113
116
  textInputProps={textInputProps}
114
- helperErrorText={helperErrorText}
115
- invalidText={invalidText}
116
- isInvalid
117
117
  />
118
118
  );
119
119
  expect(
@@ -124,12 +124,12 @@ describe("SearchBar", () => {
124
124
  it("calls the callback function on submit ", () => {
125
125
  render(
126
126
  <SearchBar
127
- labelText="searchBar"
127
+ helperErrorText={helperErrorText}
128
128
  id="id"
129
+ labelText="searchBar"
129
130
  onSubmit={searchBarSubmit}
130
131
  selectProps={selectProps}
131
132
  textInputProps={textInputProps}
132
- helperErrorText={helperErrorText}
133
133
  />
134
134
  );
135
135
  expect(searchBarSubmit).toHaveBeenCalledTimes(0);
@@ -139,34 +139,65 @@ describe("SearchBar", () => {
139
139
  expect(buttonCallback).toHaveBeenCalledTimes(1);
140
140
  });
141
141
 
142
+ it("Renders 'required' in the placeholder text", () => {
143
+ const { rerender } = render(
144
+ <SearchBar
145
+ id="requiredState"
146
+ isDisabled
147
+ isRequired
148
+ labelText="searchbar"
149
+ onSubmit={jest.fn()}
150
+ textInputProps={textInputProps}
151
+ />
152
+ );
153
+
154
+ expect(
155
+ screen.getByPlaceholderText("Item Search (Required)")
156
+ ).not.toBeInTheDocument();
157
+
158
+ rerender(
159
+ <SearchBar
160
+ id="requiredState"
161
+ isDisabled
162
+ isRequired
163
+ labelText="searchbar"
164
+ onSubmit={jest.fn()}
165
+ textInputProps={textInputProps}
166
+ />
167
+ );
168
+ expect(
169
+ screen.getByPlaceholderText("Item Search (Required)")
170
+ ).toBeInTheDocument();
171
+ });
172
+
142
173
  it("Renders the UI snapshot correctly", () => {
143
174
  const basic = renderer
144
175
  .create(
145
176
  <SearchBar
146
- id="id"
177
+ helperErrorText={helperErrorText}
178
+ id="basic"
147
179
  labelText="searchbar"
148
180
  onSubmit={jest.fn()}
149
181
  textInputProps={textInputProps}
150
- helperErrorText={helperErrorText}
151
182
  />
152
183
  )
153
184
  .toJSON();
154
185
  const withSelect = renderer
155
186
  .create(
156
187
  <SearchBar
157
- id="id"
188
+ helperErrorText={helperErrorText}
189
+ id="withSelect"
158
190
  labelText="searchbar"
159
191
  onSubmit={jest.fn()}
160
192
  selectProps={selectProps}
161
193
  textInputProps={textInputProps}
162
- helperErrorText={helperErrorText}
163
194
  />
164
195
  )
165
196
  .toJSON();
166
197
  const withoutHelperText = renderer
167
198
  .create(
168
199
  <SearchBar
169
- id="id"
200
+ id="withoutHelperText"
170
201
  labelText="searchbar"
171
202
  onSubmit={jest.fn()}
172
203
  textInputProps={textInputProps}
@@ -176,22 +207,34 @@ describe("SearchBar", () => {
176
207
  const invalidState = renderer
177
208
  .create(
178
209
  <SearchBar
179
- id="id"
210
+ id="invalidState"
211
+ isInvalid
180
212
  labelText="searchbar"
181
213
  onSubmit={jest.fn()}
182
214
  textInputProps={textInputProps}
183
- isInvalid
184
215
  />
185
216
  )
186
217
  .toJSON();
187
218
  const disabledState = renderer
188
219
  .create(
189
220
  <SearchBar
190
- id="id"
221
+ id="disabledState"
222
+ isDisabled
191
223
  labelText="searchbar"
192
224
  onSubmit={jest.fn()}
193
225
  textInputProps={textInputProps}
226
+ />
227
+ )
228
+ .toJSON();
229
+ const requiredState = renderer
230
+ .create(
231
+ <SearchBar
232
+ id="requiredState"
194
233
  isDisabled
234
+ isRequired
235
+ labelText="searchbar"
236
+ onSubmit={jest.fn()}
237
+ textInputProps={textInputProps}
195
238
  />
196
239
  )
197
240
  .toJSON();
@@ -201,5 +244,6 @@ describe("SearchBar", () => {
201
244
  expect(withoutHelperText).toMatchSnapshot();
202
245
  expect(invalidState).toMatchSnapshot();
203
246
  expect(disabledState).toMatchSnapshot();
247
+ expect(requiredState).toMatchSnapshot();
204
248
  });
205
249
  });
@@ -19,8 +19,7 @@ import DSProvider from "../../theme/provider";
19
19
  parameters={{
20
20
  design: {
21
21
  type: "figma",
22
- url:
23
- "https://www.figma.com/file/qShodlfNCJHb8n03IFyApM/Master?node-id=11689%3A423",
22
+ url: "https://www.figma.com/file/qShodlfNCJHb8n03IFyApM/Master?node-id=11689%3A423",
24
23
  },
25
24
  jest: ["SearchBar.test.tsx"],
26
25
  }}
@@ -43,7 +42,7 @@ import DSProvider from "../../theme/provider";
43
42
  | Component Version | DS Version |
44
43
  | ----------------- | ---------- |
45
44
  | Added | `0.0.4` |
46
- | Latest | `0.25.4` |
45
+ | Latest | `0.25.6` |
47
46
 
48
47
  <Description of={SearchBar} />
49
48
 
@@ -312,7 +311,7 @@ function SearchBarValueExample() {
312
311
  };
313
312
  const onSubmit = (event) => {
314
313
  event.preventDefault();
315
- console.log(`onSubmit Select value: ${event.target.searchName.value}`);
314
+ console.log(`onSubmit Select value: ${event.target.selectName.value}`);
316
315
  console.log(`onSubmit TextInput value ${event.target.textInputName.value}`);
317
316
  };
318
317
  return (
@@ -99,6 +99,9 @@ export default function SearchBar(props: SearchBarProps) {
99
99
  const ariaDescribedby = helperErrorTextID;
100
100
  const footnote = isInvalid ? invalidText : helperErrorText;
101
101
  const finalAriaLabel = footnote ? `${labelText} - ${footnote}` : labelText;
102
+ const textInputPlaceholder = `${textInputProps?.placeholder} ${
103
+ isRequired ? "(Required)" : ""
104
+ }`;
102
105
  // Render the `Select` component.
103
106
  const selectElem = selectProps && (
104
107
  <Select
@@ -120,7 +123,7 @@ export default function SearchBar(props: SearchBarProps) {
120
123
  <TextInput
121
124
  id={generateUUID()}
122
125
  labelText={textInputProps?.labelText}
123
- placeholder={textInputProps?.placeholder}
126
+ placeholder={textInputPlaceholder}
124
127
  onChange={textInputProps?.onChange}
125
128
  name={textInputProps?.name}
126
129
  type={TextInputTypes.text}
@@ -9,6 +9,8 @@ import { withDesign } from "storybook-addon-designs";
9
9
 
10
10
  import { VStack } from "@chakra-ui/react";
11
11
  import Select from "./Select";
12
+ import Form from "../Form/Form";
13
+ import Button from "../Button/Button";
12
14
  import { getCategory } from "../../utils/componentCategories";
13
15
  import DSProvider from "../../theme/provider";
14
16
 
@@ -19,8 +21,7 @@ import DSProvider from "../../theme/provider";
19
21
  parameters={{
20
22
  design: {
21
23
  type: "figma",
22
- url:
23
- "https://www.figma.com/file/qShodlfNCJHb8n03IFyApM/Master?node-id=11895%3A549",
24
+ url: "https://www.figma.com/file/qShodlfNCJHb8n03IFyApM/Master?node-id=11895%3A549",
24
25
  },
25
26
  jest: ["Select.test.tsx"],
26
27
  }}
@@ -38,7 +39,7 @@ import DSProvider from "../../theme/provider";
38
39
  | Component Version | DS Version |
39
40
  | ----------------- | ---------- |
40
41
  | Added | `0.7.0` |
41
- | Latest | `0.25.1` |
42
+ | Latest | `0.25.8` |
42
43
 
43
44
  <Description of={Select} />
44
45
 
@@ -102,8 +103,8 @@ text within the `label` element.
102
103
  <Story name="Labelling Variations">
103
104
  <VStack align="stretch" spacing={8}>
104
105
  <Select
105
- labelText="What is your favorite color?"
106
106
  helperText="Display the label"
107
+ labelText="What is your favorite color?"
107
108
  name="color"
108
109
  >
109
110
  <option value="red">Red</option>
@@ -113,8 +114,8 @@ text within the `label` element.
113
114
  <option value="white">White</option>
114
115
  </Select>
115
116
  <Select
116
- labelText="What is your favorite color?"
117
117
  helperText="Do not display the label"
118
+ labelText="What is your favorite color?"
118
119
  name="color"
119
120
  showLabel={false}
120
121
  >
@@ -125,10 +126,10 @@ text within the `label` element.
125
126
  <option value="white">White</option>
126
127
  </Select>
127
128
  <Select
128
- labelText="What is your favorite color?"
129
129
  helperText="Display the Required/Optional text"
130
- name="color"
131
130
  isRequired
131
+ labelText="What is your favorite color?"
132
+ name="color"
132
133
  >
133
134
  <option value="red">Red</option>
134
135
  <option value="green">Green</option>
@@ -137,11 +138,11 @@ text within the `label` element.
137
138
  <option value="white">White</option>
138
139
  </Select>
139
140
  <Select
140
- labelText="What is your favorite color?"
141
141
  helperText="Do not display the Required/Optional text"
142
+ isRequired
143
+ labelText="What is your favorite color?"
142
144
  name="color"
143
145
  showOptReqLabel={false}
144
- isRequired
145
146
  >
146
147
  <option value="red">Red</option>
147
148
  <option value="green">Green</option>
@@ -158,11 +159,11 @@ text within the `label` element.
158
159
  <Canvas>
159
160
  <DSProvider>
160
161
  <Select
161
- labelText="What is your favorite color?"
162
162
  helperText="This is the helper text."
163
163
  invalidText="This is the error text :("
164
- name="color"
165
164
  isInvalid
165
+ labelText="What is your favorite color?"
166
+ name="color"
166
167
  >
167
168
  <option value="red">Red</option>
168
169
  <option value="green">Green</option>
@@ -178,11 +179,11 @@ text within the `label` element.
178
179
  <Canvas>
179
180
  <DSProvider>
180
181
  <Select
181
- labelText="What is your favorite color?"
182
182
  helperText="This is the helper text."
183
183
  invalidText="This is the error text :("
184
- name="color"
185
184
  isDisabled
185
+ labelText="What is your favorite color?"
186
+ name="color"
186
187
  >
187
188
  <option value="red">Red</option>
188
189
  <option value="green">Green</option>
@@ -198,57 +199,133 @@ text within the `label` element.
198
199
  ### Controlled Component using `value` and `onChange` props
199
200
 
200
201
  If your application uses controlled React components and the DS `Select`
201
- component must be controlled, you can control and extract the data through the
202
+ component must be controlled, you can pass and extract the value through the
202
203
  `value` and `onChange` props. This will be called every time a new `option`
203
204
  value is selected.
204
205
 
206
+ Try it out: open up the browser's console to see new values being logged on
207
+ each change.
208
+
205
209
  ```jsx
206
- const value = "red";
207
- const onChange = (e) => {
208
- // This will return the value selected through the event object.
209
- console.log(e.target.value);
210
- };
211
- // ...
212
-
213
- <Select
214
- labelText="What is your favorite color?"
215
- helperText="This is the helper text."
216
- name="color"
217
- value={value}
218
- onChange={onChange}
219
- >
220
- <option value="red">Red</option>
221
- <option value="green">Green</option>
222
- <option value="blue">Blue</option>
223
- <option value="black">Black</option>
224
- <option value="white">White</option>
225
- </Select>;
210
+ export function SelectControlledExample() {
211
+ const [value, setValue] = React.useState();
212
+ const onChange = (e) => {
213
+ // This will return the value selected through the event object.
214
+ console.log(e.target.value);
215
+ setValue(e.target.value);
216
+ };
217
+ return (
218
+ <Select
219
+ helperText="This is the helper text."
220
+ labelText="What is your favorite color?"
221
+ name="color"
222
+ onChange={onChange}
223
+ value={value}
224
+ >
225
+ <option value="red">Red</option>
226
+ <option value="green">Green</option>
227
+ <option value="blue">Blue</option>
228
+ <option value="black">Black</option>
229
+ <option value="white">White</option>
230
+ </Select>
231
+ );
232
+ }
226
233
  ```
227
234
 
235
+ export function SelectControlledExample() {
236
+ const [value, setValue] = React.useState();
237
+ const onChange = (e) => {
238
+ // This will return the value selected through the event object.
239
+ console.log("Controlled value:", e.target.value);
240
+ setValue(e.target.value);
241
+ };
242
+ return (
243
+ <Select
244
+ helperText="This is the helper text."
245
+ labelText="What is your favorite color?"
246
+ name="color"
247
+ onChange={onChange}
248
+ value={value}
249
+ >
250
+ <option value="red">Red</option>
251
+ <option value="green">Green</option>
252
+ <option value="blue">Blue</option>
253
+ <option value="black">Black</option>
254
+ <option value="white">White</option>
255
+ </Select>
256
+ );
257
+ }
258
+
259
+ <Canvas>
260
+ <DSProvider>
261
+ <SelectControlledExample />
262
+ </DSProvider>
263
+ </Canvas>
264
+
228
265
  ### Uncontrolled Component using `ref`s
229
266
 
230
267
  If your application uses uncontrolled components, you can pass a React `ref`
231
- prop to the DS Select component to get the selected value from the DOM.
268
+ prop to the DS Select component to get the selected value from the DOM. Note
269
+ that this example uses a `Form` and a `Button` to submit the form, only then
270
+ will the value be available.
232
271
 
233
- ```jsx
234
- const selectRef = React.createRef<HTMLSelectElement>();
235
- // ...
236
- <Select
237
- labelText="What is your favorite color?"
238
- helperText="This is the helper text."
239
- name="color"
240
- >
241
- <option value="red">Red</option>
242
- <option value="green">Green</option>
243
- <option value="blue">Blue</option>
244
- <option value="black">Black</option>
245
- <option value="white">White</option>
246
- </Select>
272
+ Try it out: open up the browser's console to see new values being logged on
273
+ each change.
247
274
 
248
- // ...
249
- // Get the value through:
250
- const onSubmit = () => {
251
- // ...
252
- const selectValue = selectRef.current.value;
253
- };
275
+ ```jsx
276
+ export function SelectUncontrolledExample() {
277
+ const selectRef = React.createRef();
278
+ const onSubmit = () => {
279
+ const selectValue = selectRef.current.value;
280
+ console.log("Using uncontrolled ref:", selectValue);
281
+ };
282
+ return (
283
+ <Form>
284
+ <Select
285
+ helperText="This is the helper text."
286
+ labelText="What is your favorite color?"
287
+ name="color"
288
+ ref={selectRef}
289
+ >
290
+ <option value="red">Red</option>
291
+ <option value="green">Green</option>
292
+ <option value="blue">Blue</option>
293
+ <option value="black">Black</option>
294
+ <option value="white">White</option>
295
+ </Select>
296
+ <Button onClick={onSubmit}>Submit</Button>
297
+ </Form>
298
+ );
299
+ }
254
300
  ```
301
+
302
+ export function SelectUncontrolledExample() {
303
+ const selectRef = React.createRef();
304
+ const onSubmit = () => {
305
+ const selectValue = selectRef.current.value;
306
+ console.log("Using uncontrolled ref:", selectValue);
307
+ };
308
+ return (
309
+ <Form>
310
+ <Select
311
+ helperText="This is the helper text."
312
+ labelText="What is your favorite color?"
313
+ name="color"
314
+ ref={selectRef}
315
+ >
316
+ <option value="red">Red</option>
317
+ <option value="green">Green</option>
318
+ <option value="blue">Blue</option>
319
+ <option value="black">Black</option>
320
+ <option value="white">White</option>
321
+ </Select>
322
+ <Button onClick={onSubmit}>Submit</Button>
323
+ </Form>
324
+ );
325
+ }
326
+
327
+ <Canvas>
328
+ <DSProvider>
329
+ <SelectUncontrolledExample />
330
+ </DSProvider>
331
+ </Canvas>
@@ -198,7 +198,7 @@ describe("Select", () => {
198
198
  });
199
199
 
200
200
  it("calls the onChange callback function", () => {
201
- let value = "red";
201
+ let value = "";
202
202
  const changeCallback = (e) => {
203
203
  value = e.target.value;
204
204
  };
@@ -208,7 +208,7 @@ describe("Select", () => {
208
208
  </Select>
209
209
  );
210
210
 
211
- expect(value).toEqual("red");
211
+ expect(value).toEqual("");
212
212
 
213
213
  fireEvent.change(screen.getByRole("combobox"), {
214
214
  target: { value: "blue" },
@@ -38,6 +38,8 @@ export interface SelectProps {
38
38
  /** The callback function to get the selected value.
39
39
  * Should be passed along with `value` for controlled components. */
40
40
  onChange?: (event: React.FormEvent) => void;
41
+ /** Placeholder text in the select element. */
42
+ placeholder?: string;
41
43
  /** Offers the ability to hide the helper/invalid text. */
42
44
  showHelperInvalidText?: boolean;
43
45
  /** Offers the ability to show the select's label onscreen or hide it. Refer
@@ -72,11 +74,12 @@ const Select = React.forwardRef<
72
74
  labelText,
73
75
  name,
74
76
  onChange,
77
+ placeholder,
75
78
  showHelperInvalidText = true,
76
79
  showLabel = true,
77
80
  showOptReqLabel = true,
78
81
  type = SelectTypes.Default,
79
- value,
82
+ value = "",
80
83
  } = props;
81
84
  const ariaAttributes = {};
82
85
  const optReqFlag = isRequired ? "Required" : "Optional";
@@ -87,7 +90,7 @@ const Select = React.forwardRef<
87
90
  const footnote = isInvalid ? finalInvalidText : helperText;
88
91
  // To control the `Select` component, both `onChange` and `value`
89
92
  // must be passed.
90
- const controlledProps = onChange && value ? { onChange, value } : {};
93
+ const controlledProps = onChange ? { onChange, value } : {};
91
94
 
92
95
  if (!showLabel) {
93
96
  ariaAttributes["aria-label"] =
@@ -126,6 +129,7 @@ const Select = React.forwardRef<
126
129
  isDisabled={isDisabled}
127
130
  isInvalid={isInvalid}
128
131
  name={name}
132
+ placeholder={placeholder}
129
133
  ref={ref}
130
134
  {...controlledProps}
131
135
  {...ariaAttributes}
@@ -40,7 +40,7 @@ import DSProvider from "../../theme/provider";
40
40
  | Component Version | DS Version |
41
41
  | ----------------- | ---------- |
42
42
  | Added | `0.25.4` |
43
- | Latest | `0.25.4` |
43
+ | Latest | `0.25.8` |
44
44
 
45
45
  <Description of={Slider} />
46
46
 
@@ -439,7 +439,8 @@ Pass a callback function to the `onChange` prop to get the current number value
439
439
  of the `Slider` component or an array of two numbers when it is a range slider.
440
440
  Internally, the `Slider` component handles the state of the current selected
441
441
  value or values. Once the value(s) is updated, the `onChange` callback will be
442
- called and the values will be passed.
442
+ called and the values will be passed. If no `onChange` callback is provided,
443
+ you won't be able to get the updated value(s) of the `Slider` component.
443
444
 
444
445
  #### Single Slider Value
445
446
 
@@ -294,6 +294,26 @@ describe("Slider", () => {
294
294
  expect(input).toHaveValue(100);
295
295
  });
296
296
 
297
+ it("doesn't crash if no onChange callback function is passed", () => {
298
+ let currentValue = 0;
299
+ render(
300
+ <Slider
301
+ defaultValue={50}
302
+ helperText="Component helper text."
303
+ invalidText="Component error text :("
304
+ labelText="Label"
305
+ />
306
+ );
307
+
308
+ const input = screen.getByRole("spinbutton");
309
+ fireEvent.change(input, {
310
+ target: { value: "42" },
311
+ });
312
+ // While we change the slider input value, since there is no onChange
313
+ // function, there is no way to update this local `currentValue` variable.
314
+ expect(currentValue).toEqual(0);
315
+ });
316
+
297
317
  it("gets the current value through the onChange callback function", () => {
298
318
  let currentValue = 0;
299
319
  function onChange(value) {
@@ -495,6 +515,21 @@ describe("Slider", () => {
495
515
  );
496
516
  });
497
517
 
518
+ it("renders with min and max values as the default values if no `defaultValue` array is passed", () => {
519
+ render(
520
+ <Slider
521
+ helperText="Component helper text."
522
+ invalidText="Component error text :("
523
+ isRangeSlider
524
+ labelText="Label"
525
+ max={80}
526
+ min={30}
527
+ />
528
+ );
529
+ expect(screen.getByText("30")).toBeInTheDocument();
530
+ expect(screen.getByText("80")).toBeInTheDocument();
531
+ });
532
+
498
533
  it("renders the invalid state if the start and end values are wrong", () => {
499
534
  // The start value is bigger than the end value.
500
535
  render(
@@ -97,8 +97,14 @@ export default function Slider(props: React.PropsWithChildren<SliderProps>) {
97
97
  showValues = true,
98
98
  step = 1,
99
99
  } = props;
100
+ // For the RangeSlider, if the defaultValue is not an array, then we set
101
+ // the defaultValue to an array with the min and max values.
102
+ const rangeSliderDefault =
103
+ typeof defaultValue === "number" ? [min, max] : defaultValue;
104
+ // We need to set the default value correctly for both types of sliders.
105
+ const finalDevaultValue = isRangeSlider ? rangeSliderDefault : defaultValue;
100
106
  const [currentValue, setCurrentValue] = React.useState<typeof defaultValue>(
101
- defaultValue
107
+ finalDevaultValue
102
108
  );
103
109
  let finalIsInvalid = isInvalid;
104
110
  // In the Range Slider, if the first value is bigger than the second value,
@@ -127,7 +133,7 @@ export default function Slider(props: React.PropsWithChildren<SliderProps>) {
127
133
  onChange: (val) => setCurrentValue(val),
128
134
  // Call the passed in `onChange` function prop to get the
129
135
  // *final* value once a user stops dragging the slider.
130
- onChangeEnd: (val) => onChange(val),
136
+ onChangeEnd: (val) => onChange && onChange(val),
131
137
  step,
132
138
  };
133
139
  // Props that the two `TextInput` components use.