@khanacademy/wonder-blocks-form 2.4.3 → 2.4.6

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.
@@ -1,5 +1,6 @@
1
1
  // @flow
2
2
  import * as React from "react";
3
+ import {StyleSheet} from "aphrodite";
3
4
 
4
5
  import {LabeledTextField} from "@khanacademy/wonder-blocks-form";
5
6
  import {View} from "@khanacademy/wonder-blocks-core";
@@ -8,12 +9,46 @@ import Color from "@khanacademy/wonder-blocks-color";
8
9
  import Spacing from "@khanacademy/wonder-blocks-spacing";
9
10
  import {Strut} from "@khanacademy/wonder-blocks-layout";
10
11
  import Button from "@khanacademy/wonder-blocks-button";
11
- import {StyleSheet} from "aphrodite";
12
12
 
13
13
  import type {StoryComponentType} from "@storybook/react";
14
14
 
15
+ import ComponentInfo from "../../../../../.storybook/components/component-info.js";
16
+ import {name, version} from "../../../package.json";
17
+ import LabeledTextFieldArgTypes from "./labeled-text-field.argtypes.js";
18
+
15
19
  export default {
16
20
  title: "Form / LabeledTextField",
21
+ component: LabeledTextField,
22
+ parameters: {
23
+ componentSubtitle: ((
24
+ <ComponentInfo name={name} version={version} />
25
+ ): any),
26
+ },
27
+ argTypes: LabeledTextFieldArgTypes,
28
+ };
29
+
30
+ export const Default: StoryComponentType = (args) => {
31
+ return <LabeledTextField {...args} />;
32
+ };
33
+
34
+ Default.args = {
35
+ id: "some-ltf-id",
36
+ type: "text",
37
+ label: "Label",
38
+ description: "Hello, this is the description for this field",
39
+ value: "",
40
+ disabled: false,
41
+ required: false,
42
+ light: false,
43
+ placeholder: "Placeholder",
44
+ readOnly: false,
45
+ autoComplete: "off",
46
+ validate: () => {},
47
+ onValidate: () => {},
48
+ onChange: () => {},
49
+ onKeyDown: () => {},
50
+ onFocus: () => {},
51
+ onBlur: () => {},
17
52
  };
18
53
 
19
54
  export const Text: StoryComponentType = () => {
@@ -37,6 +72,13 @@ export const Text: StoryComponentType = () => {
37
72
  );
38
73
  };
39
74
 
75
+ Text.parameters = {
76
+ docs: {
77
+ storyDescription:
78
+ "An input field with type `text` takes all kinds of characters.",
79
+ },
80
+ };
81
+
40
82
  export const RequiredWithDefaultText: StoryComponentType = () => {
41
83
  const [value, setValue] = React.useState("");
42
84
 
@@ -51,13 +93,29 @@ export const RequiredWithDefaultText: StoryComponentType = () => {
51
93
  label="Name"
52
94
  description="Please enter your name"
53
95
  value={value}
54
- onChange={(newValue) => setValue(newValue)}
96
+ onChange={setValue}
55
97
  onKeyDown={handleKeyDown}
56
98
  required={true}
57
99
  />
58
100
  );
59
101
  };
60
102
 
103
+ RequiredWithDefaultText.parameters = {
104
+ docs: {
105
+ storyDescription: `A required field will show the message
106
+ "This field is required." by default if someone types in it
107
+ at some point but leaves it blank. Type in the field, then
108
+ backspace all the way and click out of the field to see
109
+ this message. Note that this message would not appear if
110
+ the \`validation\` prop were set.`,
111
+ },
112
+ chromatic: {
113
+ // Disabling snapshot because it doesn't show the error message
114
+ // until after the user interacts with this field.
115
+ disableSnapshot: true,
116
+ },
117
+ };
118
+
61
119
  export const RequiredWithSpecifiedText: StoryComponentType = () => {
62
120
  const [value, setValue] = React.useState("");
63
121
 
@@ -72,7 +130,7 @@ export const RequiredWithSpecifiedText: StoryComponentType = () => {
72
130
  label="Name"
73
131
  description="Please enter your name"
74
132
  value={value}
75
- onChange={(newValue) => setValue(newValue)}
133
+ onChange={setValue}
76
134
  onKeyDown={handleKeyDown}
77
135
  required="This specific field is super required."
78
136
  />
@@ -80,8 +138,16 @@ export const RequiredWithSpecifiedText: StoryComponentType = () => {
80
138
  };
81
139
 
82
140
  RequiredWithSpecifiedText.parameters = {
141
+ docs: {
142
+ storyDescription: `If a string is passed into the \`required\` prop,
143
+ the specified message will show when the field is left blank.
144
+ Type in the field, then backspace all the way and click out of
145
+ the field to see this message. Note that this message would not
146
+ appear if the \`validation\` prop were set.`,
147
+ },
83
148
  chromatic: {
84
- // We have screenshots of other stories that cover this case.
149
+ // Disabling snapshot because it doesn't show the error message
150
+ // until after the user interacts with this field.
85
151
  disableSnapshot: true,
86
152
  },
87
153
  };
@@ -101,15 +167,22 @@ export const Number: StoryComponentType = () => {
101
167
  type="number"
102
168
  description="Please enter your age"
103
169
  value={value}
104
- onChange={(newValue) => setValue(newValue)}
170
+ onChange={setValue}
105
171
  placeholder="Age"
106
172
  onKeyDown={handleKeyDown}
107
173
  />
108
174
  );
109
175
  };
110
176
 
177
+ Number.parameters = {
178
+ docs: {
179
+ storyDescription:
180
+ "An input field with type `number` will only take numeric characters as input.",
181
+ },
182
+ };
183
+
111
184
  export const Password: StoryComponentType = () => {
112
- const [value, setValue] = React.useState("Password123");
185
+ const [value, setValue] = React.useState("$ecure123");
113
186
 
114
187
  const validate = (value: string) => {
115
188
  if (value.length < 8) {
@@ -132,7 +205,7 @@ export const Password: StoryComponentType = () => {
132
205
  type="password"
133
206
  description="Please enter a secure password"
134
207
  value={value}
135
- onChange={(newValue) => setValue(newValue)}
208
+ onChange={setValue}
136
209
  placeholder="Password"
137
210
  validate={validate}
138
211
  onKeyDown={handleKeyDown}
@@ -140,6 +213,15 @@ export const Password: StoryComponentType = () => {
140
213
  );
141
214
  };
142
215
 
216
+ Password.parameters = {
217
+ docs: {
218
+ storyDescription: `An input field with type \`password\` will
219
+ obscure the input value. It also often contains validation.
220
+ In this example, the password must be over 8 characters long and
221
+ must contain a numeric value.`,
222
+ },
223
+ };
224
+
143
225
  export const Email: StoryComponentType = () => {
144
226
  const [value, setValue] = React.useState("khan@khan.org");
145
227
 
@@ -161,7 +243,7 @@ export const Email: StoryComponentType = () => {
161
243
  label="Email"
162
244
  type="email"
163
245
  value={value}
164
- onChange={(newValue) => setValue(newValue)}
246
+ onChange={setValue}
165
247
  description="Please provide your personal email"
166
248
  placeholder="Email"
167
249
  validate={validate}
@@ -170,6 +252,15 @@ export const Email: StoryComponentType = () => {
170
252
  );
171
253
  };
172
254
 
255
+ Email.parameters = {
256
+ docs: {
257
+ storyDescription: `An input field with type \`email\` will automatically
258
+ validate an input on submit to ensure it's either formatted properly
259
+ or blank. \`TextField\` will run validation on blur if the
260
+ \`validate\` prop is passed in, as in this example.`,
261
+ },
262
+ };
263
+
173
264
  export const EmailRequired: StoryComponentType = () => {
174
265
  const [value, setValue] = React.useState("");
175
266
 
@@ -190,7 +281,7 @@ export const EmailRequired: StoryComponentType = () => {
190
281
  <LabeledTextField
191
282
  label="Email"
192
283
  type="email"
193
- onChange={(newValue) => setValue(newValue)}
284
+ onChange={setValue}
194
285
  description="Please provide your personal email"
195
286
  value={value}
196
287
  validate={validate}
@@ -201,6 +292,13 @@ export const EmailRequired: StoryComponentType = () => {
201
292
  };
202
293
 
203
294
  EmailRequired.parameters = {
295
+ docs: {
296
+ storyDescription: `An example of a required field that also has
297
+ a \`validation\` prop passed in. \`required\` can be a boolean or
298
+ a string. In this case, \`required\` is set to \`true\` since a
299
+ string would not even be used if it were passed in because the
300
+ validation overrides it.`,
301
+ },
204
302
  chromatic: {
205
303
  // We have screenshots of other stories that cover this case.
206
304
  disableSnapshot: true,
@@ -228,7 +326,7 @@ export const Telephone: StoryComponentType = () => {
228
326
  label="Telephone"
229
327
  type="tel"
230
328
  value={value}
231
- onChange={(newValue) => setValue(newValue)}
329
+ onChange={setValue}
232
330
  description="Please provide your personal phone number"
233
331
  placeholder="Telephone"
234
332
  validate={validate}
@@ -237,6 +335,15 @@ export const Telephone: StoryComponentType = () => {
237
335
  );
238
336
  };
239
337
 
338
+ Telephone.parameters = {
339
+ docs: {
340
+ storyDescription: `An input field with type \`tel\` will NOT
341
+ validate an input on submit by default as telephone numbers
342
+ can vary considerably. \`TextField\` will run validation on blur
343
+ if the \`validate\` prop is passed in, as in this example.`,
344
+ },
345
+ };
346
+
240
347
  export const Error: StoryComponentType = () => {
241
348
  const [value, setValue] = React.useState("khan");
242
349
 
@@ -258,7 +365,7 @@ export const Error: StoryComponentType = () => {
258
365
  label="Email"
259
366
  type="email"
260
367
  value={value}
261
- onChange={(newValue) => setValue(newValue)}
368
+ onChange={setValue}
262
369
  description="Please provide your personal email"
263
370
  placeholder="Email"
264
371
  validate={validate}
@@ -267,16 +374,12 @@ export const Error: StoryComponentType = () => {
267
374
  );
268
375
  };
269
376
 
270
- export const Disabled: StoryComponentType = () => (
271
- <LabeledTextField
272
- label="Name"
273
- description="Please enter your name"
274
- value=""
275
- onChange={() => {}}
276
- placeholder="Name"
277
- disabled={true}
278
- />
279
- );
377
+ Error.parameters = {
378
+ docs: {
379
+ storyDescription: `If an input value fails validation,
380
+ \`TextField\` will have error styling.`,
381
+ },
382
+ };
280
383
 
281
384
  export const Light: StoryComponentType = () => {
282
385
  const [value, setValue] = React.useState("");
@@ -299,7 +402,7 @@ export const Light: StoryComponentType = () => {
299
402
  </LabelSmall>
300
403
  }
301
404
  value={value}
302
- onChange={(newValue) => setValue(newValue)}
405
+ onChange={setValue}
303
406
  placeholder="Name"
304
407
  light={true}
305
408
  onKeyDown={handleKeyDown}
@@ -308,6 +411,80 @@ export const Light: StoryComponentType = () => {
308
411
  );
309
412
  };
310
413
 
414
+ Light.parameters = {
415
+ docs: {
416
+ storyDescription: `If the \`light\` prop is set to true, the
417
+ underlying \`TextField\` will have a light border when focused.
418
+ This is intended to be used on a dark background. There is also a
419
+ specific light styling for the error state, as seen in the
420
+ \`ErrorLight\` story.`,
421
+ },
422
+ };
423
+
424
+ export const ErrorLight: StoryComponentType = () => {
425
+ const [value, setValue] = React.useState("khan");
426
+
427
+ const validate = (value: string) => {
428
+ const emailRegex = /^[^@\s]+@[^@\s.]+\.[^@.\s]+$/;
429
+ if (!emailRegex.test(value)) {
430
+ return "Please enter a valid email";
431
+ }
432
+ };
433
+
434
+ const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
435
+ if (event.key === "Enter") {
436
+ event.currentTarget.blur();
437
+ }
438
+ };
439
+
440
+ return (
441
+ <View style={styles.darkBackground}>
442
+ <LabeledTextField
443
+ label={
444
+ <LabelMedium style={styles.whiteColor}>Email</LabelMedium>
445
+ }
446
+ description={
447
+ <LabelSmall style={styles.offWhiteColor}>
448
+ Please provide your personal email
449
+ </LabelSmall>
450
+ }
451
+ type="email"
452
+ value={value}
453
+ light={true}
454
+ onChange={setValue}
455
+ placeholder="Email"
456
+ validate={validate}
457
+ onKeyDown={handleKeyDown}
458
+ />
459
+ </View>
460
+ );
461
+ };
462
+
463
+ ErrorLight.parameters = {
464
+ docs: {
465
+ storyDescription: `If an input value fails validation and the
466
+ \`light\` prop is true, \`TextField\` will have light error styling.`,
467
+ },
468
+ };
469
+
470
+ export const Disabled: StoryComponentType = () => (
471
+ <LabeledTextField
472
+ label="Name"
473
+ description="Please enter your name"
474
+ value=""
475
+ onChange={() => {}}
476
+ placeholder="Name"
477
+ disabled={true}
478
+ />
479
+ );
480
+
481
+ Disabled.parameters = {
482
+ docs: {
483
+ storyDescription: `If the \`disabled\` prop is set to true,
484
+ \`TextField\` will have disabled styling and will not be interactable.`,
485
+ },
486
+ };
487
+
311
488
  export const CustomStyle: StoryComponentType = () => {
312
489
  const [firstName, setFirstName] = React.useState("");
313
490
  const [lastName, setLastName] = React.useState("");
@@ -324,7 +501,7 @@ export const CustomStyle: StoryComponentType = () => {
324
501
  label="First name"
325
502
  description="Please enter your first name"
326
503
  value={firstName}
327
- onChange={(newValue) => setFirstName(newValue)}
504
+ onChange={setFirstName}
328
505
  placeholder="Khan"
329
506
  style={styles.grow}
330
507
  onKeyDown={handleKeyDown}
@@ -334,7 +511,7 @@ export const CustomStyle: StoryComponentType = () => {
334
511
  label="Last name"
335
512
  description="Please enter your last name"
336
513
  value={lastName}
337
- onChange={(newValue) => setLastName(newValue)}
514
+ onChange={setLastName}
338
515
  placeholder="Academy"
339
516
  style={styles.grow}
340
517
  onKeyDown={handleKeyDown}
@@ -343,6 +520,14 @@ export const CustomStyle: StoryComponentType = () => {
343
520
  );
344
521
  };
345
522
 
523
+ CustomStyle.parameters = {
524
+ docs: {
525
+ storyDescription: `\`LabeledTextField\` can take in custom styles that
526
+ override the default styles. In this example, each field has the
527
+ style property \`flexGrow: 1\``,
528
+ },
529
+ };
530
+
346
531
  export const Ref: StoryComponentType = () => {
347
532
  const [value, setValue] = React.useState("Khan");
348
533
  const inputRef = React.createRef();
@@ -365,7 +550,7 @@ export const Ref: StoryComponentType = () => {
365
550
  label="Name"
366
551
  description="Please enter your name"
367
552
  value={value}
368
- onChange={(newValue) => setValue(newValue)}
553
+ onChange={setValue}
369
554
  placeholder="Name"
370
555
  onKeyDown={handleKeyDown}
371
556
  ref={inputRef}
@@ -378,6 +563,23 @@ export const Ref: StoryComponentType = () => {
378
563
  );
379
564
  };
380
565
 
566
+ Ref.parameters = {
567
+ docs: {
568
+ storyDescription: `If you need to save a reference to the input
569
+ field, you can do so by using the \`ref\` prop. In this example,
570
+ we want the input field to receive focus when the button is
571
+ pressed. We can do this by creating a React ref of type
572
+ \`HTMLInputElement\` and passing it into \`TextField\`'s \`ref\` prop.
573
+ Now we can use the ref variable in the \`handleSubmit\` function to
574
+ shift focus to the field.`,
575
+ chromatic: {
576
+ // Disabling snapshot because this is testing interaction,
577
+ // not visuals.
578
+ disableSnapshot: true,
579
+ },
580
+ },
581
+ };
582
+
381
583
  export const ReadOnly: StoryComponentType = () => {
382
584
  const [value, setValue] = React.useState("Khan");
383
585
 
@@ -392,7 +594,7 @@ export const ReadOnly: StoryComponentType = () => {
392
594
  label="Read Only"
393
595
  description="This is a read-only field."
394
596
  value={value}
395
- onChange={(newValue) => setValue(newValue)}
597
+ onChange={setValue}
396
598
  placeholder="Name"
397
599
  onKeyDown={handleKeyDown}
398
600
  readOnly={true}
@@ -400,6 +602,20 @@ export const ReadOnly: StoryComponentType = () => {
400
602
  );
401
603
  };
402
604
 
605
+ ReadOnly.parameters = {
606
+ docs: {
607
+ storyDescription: `An input field with the prop \`readOnly\` set
608
+ to true is not interactable. It looks the same as if it were not
609
+ read only, and it can still receive focus, but the interaction
610
+ point will not appear and the input will not change.`,
611
+ chromatic: {
612
+ // Disabling snapshot because this is testing interaction,
613
+ // not visuals.
614
+ disableSnapshot: true,
615
+ },
616
+ },
617
+ };
618
+
403
619
  export const AutoComplete: StoryComponentType = () => {
404
620
  const [value, setValue] = React.useState("");
405
621
 
@@ -410,18 +626,38 @@ export const AutoComplete: StoryComponentType = () => {
410
626
  };
411
627
 
412
628
  return (
413
- <LabeledTextField
414
- label="Name"
415
- description="Please enter your name."
416
- value={value}
417
- onChange={(newValue) => setValue(newValue)}
418
- placeholder="Name"
419
- onKeyDown={handleKeyDown}
420
- autoComplete="name"
421
- />
629
+ <form>
630
+ <LabeledTextField
631
+ label="Name"
632
+ description="Please enter your name."
633
+ value={value}
634
+ onChange={setValue}
635
+ placeholder="Name"
636
+ onKeyDown={handleKeyDown}
637
+ style={styles.fieldWithButton}
638
+ autoComplete="name"
639
+ />
640
+ <Button type="submit">Submit</Button>
641
+ </form>
422
642
  );
423
643
  };
424
644
 
645
+ AutoComplete.parameters = {
646
+ docs: {
647
+ storyDescription: `If \`TextField\`'s \`autocomplete\` prop is set,
648
+ the browser can predict values for the input. When the user starts
649
+ to type in the field, a list of options will show up based on
650
+ values that may have been submitted at a previous time.
651
+ In this example, the text field provides options after you
652
+ input a value, press the submit button, and refresh the page.`,
653
+ chromatic: {
654
+ // Disabling snapshot because this is testing interaction,
655
+ // not visuals.
656
+ disableSnapshot: true,
657
+ },
658
+ },
659
+ };
660
+
425
661
  const styles = StyleSheet.create({
426
662
  darkBackground: {
427
663
  background: Color.darkBlue,
@@ -442,4 +678,7 @@ const styles = StyleSheet.create({
442
678
  grow: {
443
679
  flexGrow: 1,
444
680
  },
681
+ fieldWithButton: {
682
+ marginBottom: Spacing.medium_16,
683
+ },
445
684
  });
@@ -0,0 +1,182 @@
1
+ // @flow
2
+ import * as React from "react";
3
+ import {StyleSheet} from "aphrodite";
4
+
5
+ import {Choice, RadioGroup} from "@khanacademy/wonder-blocks-form";
6
+ import {LabelLarge} from "@khanacademy/wonder-blocks-typography";
7
+
8
+ import type {StoryComponentType} from "@storybook/react";
9
+
10
+ import ComponentInfo from "../../../../../.storybook/components/component-info.js";
11
+ import {name, version} from "../../../package.json";
12
+
13
+ export default {
14
+ title: "Form / RadioGroup",
15
+ component: RadioGroup,
16
+ parameters: {
17
+ componentSubtitle: ((
18
+ <ComponentInfo name={name} version={version} />
19
+ ): any),
20
+ },
21
+ };
22
+
23
+ export const Default: StoryComponentType = (args) => {
24
+ return (
25
+ <RadioGroup {...args}>
26
+ <Choice label="Bulbasaur" value="bulbasaur" />
27
+ <Choice
28
+ label="Charmander"
29
+ value="charmander"
30
+ description="Oops, we ran out of Charmanders"
31
+ disabled
32
+ />
33
+ <Choice label="Squirtle" value="squirtle" />
34
+ <Choice label="Pikachu" value="pikachu" />
35
+ </RadioGroup>
36
+ );
37
+ };
38
+
39
+ Default.args = {
40
+ // Required
41
+ groupName: "pokemon",
42
+ selectedValue: "bulbasaur",
43
+ onChange: () => {},
44
+ // Optional
45
+ label: "Pokemon",
46
+ description: "Your first Pokemon.",
47
+ };
48
+
49
+ export const Basic: StoryComponentType = () => {
50
+ const [selectedValue, setSelectedValue] = React.useState("");
51
+
52
+ return (
53
+ <RadioGroup
54
+ groupName="pokemon"
55
+ label="Pokemon"
56
+ description="Your first Pokemon."
57
+ onChange={setSelectedValue}
58
+ selectedValue={selectedValue}
59
+ >
60
+ <Choice label="Bulbasaur" value="bulbasaur" />
61
+ <Choice
62
+ label="Charmander"
63
+ value="charmander"
64
+ description="Oops, we ran out of Charmanders"
65
+ disabled
66
+ />
67
+ <Choice label="Squirtle" value="squirtle" />
68
+ <Choice label="Pikachu" value="pikachu" />
69
+ </RadioGroup>
70
+ );
71
+ };
72
+
73
+ Basic.parameters = {
74
+ docs: {
75
+ storyDescription: `This is a basic example of a radio group.
76
+ The Wonder Blocks \`RadioGroup\` component takes \`Choice\`
77
+ components as children. One of the \`Choice\` components here
78
+ includes a description.`,
79
+ },
80
+ };
81
+
82
+ export const Error: StoryComponentType = () => {
83
+ const emptyError = "You must select an option to continue.";
84
+ const [selectedValue, setSelectedValue] = React.useState("");
85
+ const [error, setError] = React.useState(emptyError);
86
+
87
+ // This returns an error message if no option is selected,
88
+ // and it returns undefined otherwise. We use undefined instead of
89
+ // null here because null would result in a flow error, whereas
90
+ // undefined would be the same as not passing in anything to the
91
+ // radio group's `errorMessage` prop.
92
+ const checkForError = (input) => {
93
+ if (!input) {
94
+ return emptyError;
95
+ }
96
+ };
97
+
98
+ const handleChange = (input) => {
99
+ const errorMessage = checkForError(input);
100
+ setSelectedValue(input);
101
+ setError(errorMessage);
102
+ };
103
+
104
+ return (
105
+ <RadioGroup
106
+ groupName="pokemon"
107
+ label="Pokemon"
108
+ description="Your first Pokemon."
109
+ onChange={handleChange}
110
+ selectedValue={selectedValue}
111
+ errorMessage={error}
112
+ >
113
+ <Choice label="Bulbasaur" value="bulbasaur" />
114
+ <Choice label="Charmander" value="charmander" />
115
+ <Choice label="Squirtle" value="squirtle" />
116
+ <Choice label="Pikachu" value="pikachu" />
117
+ </RadioGroup>
118
+ );
119
+ };
120
+
121
+ Error.parameters = {
122
+ docs: {
123
+ storyDescription: `This is what a radio group looks like
124
+ if it has an error. It displays the error that is passed into the
125
+ \`errorMessage\` prop, provided the error is not null. It also
126
+ uses the error styling for all the radio buttons. Here, the error
127
+ message is saved as a state, updated in the change handler, and then
128
+ passed in as the \`errorMessage\` prop.`,
129
+ },
130
+ };
131
+
132
+ export const MultipleChoiceStyling: StoryComponentType = () => {
133
+ const [selectedValue, setSelectedValue] = React.useState("");
134
+
135
+ return (
136
+ <>
137
+ <LabelLarge style={styles.prompt}>
138
+ Select your blood type
139
+ </LabelLarge>
140
+ <RadioGroup
141
+ groupName="science-classes"
142
+ onChange={setSelectedValue}
143
+ selectedValue={selectedValue}
144
+ >
145
+ <Choice label="A" value="1" style={styles.choice} />
146
+ <Choice label="B" value="2" style={styles.choice} />
147
+ <Choice label="AB" value="3" style={styles.choice} />
148
+ <Choice
149
+ label="O"
150
+ value="4"
151
+ style={[styles.choice, styles.lastChoice]}
152
+ />
153
+ </RadioGroup>
154
+ </>
155
+ );
156
+ };
157
+
158
+ MultipleChoiceStyling.parameters = {
159
+ docs: {
160
+ storyDescription: `This example shows how to use custom styling
161
+ to change the appearance of the radio group to look more like
162
+ a multiple choice question. Here, there is a line in
163
+ between each question, which is achieved using the
164
+ \`{borderTop: "solid 1px #CCC"}\` style on each \`Choice\`
165
+ component.`,
166
+ },
167
+ };
168
+
169
+ const styles = StyleSheet.create({
170
+ choice: {
171
+ margin: 0,
172
+ height: 48,
173
+ borderTop: "solid 1px #CCC",
174
+ justifyContent: "center",
175
+ },
176
+ lastChoice: {
177
+ borderBottom: "solid 1px #CCC",
178
+ },
179
+ prompt: {
180
+ marginBottom: 16,
181
+ },
182
+ });