Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple Choice Refactor #366

Merged
merged 4 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
// cspell:ignore autodocs
import { StorybookConfig } from '@storybook/react-vite';
import { StorybookConfig } from "@storybook/react-vite";

const config: StorybookConfig = {
core: {
disableTelemetry: true
},
core: {
disableTelemetry: true,
},

docs: {
autodocs: true
},
docs: {
autodocs: true,
},

framework: {
name: "@storybook/react-vite",
options: {
framework: {
name: "@storybook/react-vite",
options: {
strictMode: true,
},
},
},

stories: ['../packages/react/src/**/*.stories.{ts,tsx,mdx}'],

addons: [
'@storybook/addon-essentials',
'@storybook/addon-a11y',
stories: [
"../packages/react/src/**/*.mdx",
"../packages/react/src/**/*.stories.@(js|jsx|mjs|ts|tsx)",
],

addons: ["@storybook/addon-essentials", "@storybook/addon-a11y"],
};

export default config;
1 change: 0 additions & 1 deletion packages/core/src/_system.scss
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
@forward 'components/checkbox' as checkbox-*;
@forward 'components/disclosure' as disclosure-*;
@forward 'components/dropdown' as dropdown-*;
@forward 'components/feedback-modal' as feedback-modal-*;
@forward 'components/field' as field-*;
@forward 'components/icon' as icon-*;
@forward 'components/link' as link-*;
Expand Down
34 changes: 0 additions & 34 deletions packages/core/src/components/feedback-modal/index.scss

This file was deleted.

35 changes: 33 additions & 2 deletions packages/core/src/components/multiple-choice/index.scss
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
@use 'tokens';
@use '../../util';

@mixin style {
@include util.declare('multiple-choice') {
.nds-multiple-choice {
&__intro {
// Styles for the question view
.nds-mc-question {
&__framing {
margin-bottom: 0.25rem;
}

Expand Down Expand Up @@ -44,5 +46,34 @@
font-weight: bold;
}
}

// Styles for the feedback view, which extends the modal dialog
.nds-mc-feedback {
&__container {
display: flex;
gap: 1rem;
}

&__icon-container {
min-width: tokens.$icon-width;
}

&__icon {
width: tokens.$icon-width;
height: tokens.$icon-width;

&--correct {
color: tokens.$correct;
}

&--incorrect {
color: tokens.$incorrect;
}
}

&__heading {
margin-bottom: 0;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
$name: 'feedback-modal';
$icon-width: 2.5rem;
$correct: var(--nds-green-60);
$incorrect: var(--nds-red-60);
1 change: 0 additions & 1 deletion packages/core/src/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@
@include nds.checkbox-style;
@include nds.disclosure-style;
@include nds.dropdown-style;
@include nds.feedback-modal-style;
@include nds.field-style;
@include nds.icon-style;
@include nds.link-style;
Expand Down
2 changes: 0 additions & 2 deletions packages/react/src/components/FeedbackModal/index.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/react/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ export * from './Checkbox';
export * from './ChoiceField';
export * from './Disclosure';
export * from './Dropdown';
export * from './FeedbackModal';
export * from './Field';
export * from './Icon';
export * from './Link';
Expand Down
5 changes: 3 additions & 2 deletions packages/react/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// Public components
export * from './components';

// Public patterns
export * from './patterns';

// Public providers
export * from './providers';

// Public utilities
export * from './utilities';

export * from './patterns';
28 changes: 0 additions & 28 deletions packages/react/src/patterns/MultipleChoice/AnswerChoice.tsx

This file was deleted.

21 changes: 21 additions & 0 deletions packages/react/src/patterns/MultipleChoice/MultipleChoice.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ArgsTable, Canvas, Meta } from '@storybook/blocks';
import { MultipleChoiceQuestion, MultipleChoiceFeedback } from '.';
import * as MultipleChoiceStories from './MultipleChoice.stories';

<Meta of={MultipleChoiceStories} />

# Multiple Choice

The multiple choice pattern exposes a component for the question view and a component for the feedback view.

## Question view props

The `<MultipleChoiceQuestion>` component is the initial view for the pattern.

<ArgsTable of={MultipleChoiceQuestion} />

## Feedback view props

The `<MultipleChoiceFeedback>` view extends our [Modal dialog](?path=/docs/modal--docs) and should be shown after the user answers the question.

<ArgsTable of={MultipleChoiceFeedback} />
114 changes: 114 additions & 0 deletions packages/react/src/patterns/MultipleChoice/MultipleChoice.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { MultipleChoiceQuestion, MultipleChoiceFeedback, useMultipleChoice } from '.';
import { Button } from '../../components/Button';
import { Modal } from '../../components/Modal';

/**
* These props are not part of the Multiple Choice pattern, but provide an example
* of how users might implement functionality on top of the pattern.
*/
interface CustomStoryProps {
attemptCount?: number;
}

type StoryProps = Omit<React.ComponentProps<typeof MultipleChoiceQuestion>, 'instructions'> &
CustomStoryProps;

const MultipleChoice = ({ attemptCount, ...args }: StoryProps) => {
const [readOnly, setReadOnly] = useState(false);
const [modalOpen, setModalOpen] = useState(false);
const [remainingAttempts, setRemainingAttempts] = useState(attemptCount || 1);
const { questionState, setStatus, modalState } = useMultipleChoice(args.choices);

const handleSubmit = useCallback(
(event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();

switch (questionState.selected) {
case undefined: {
setModalOpen(true);
setStatus('unanswered');
break;
}
case 1: {
setStatus('correct');
setRemainingAttempts(0);
break;
}
default: {
setStatus('incorrect');
if (attemptCount !== undefined) {
setRemainingAttempts(remainingAttempts - 1);
}
}
}
},
[setStatus, questionState.selected, attemptCount, remainingAttempts],
);

useEffect(() => {
if (remainingAttempts === 0) setReadOnly(true);
}, [remainingAttempts]);

const instructions = useMemo(() => {
if (attemptCount === undefined) {
return undefined;
}
if (questionState.status === 'correct') {
return 'Correct answer selected!';
}
return `Select an answer. You have ${remainingAttempts} attempts remaining.`;
}, [questionState.status, attemptCount, remainingAttempts]);

return (
<form onSubmit={handleSubmit}>
<MultipleChoiceQuestion
instructions={instructions}
readOnly={readOnly}
{...args}
{...questionState}
/>
<MultipleChoiceFeedback {...modalState} />
<div>
<Button type="submit" variant="solid" color="cyan">
Submit
</Button>
</div>
<Modal
title="Selection required"
isOpen={modalOpen}
onRequestClose={() => setModalOpen(false)}
>
Please select an answer to receive feedback.
</Modal>
</form>
);
};

const meta: Meta<StoryProps> = {
title: 'Patterns/Multiple Choice',
component: MultipleChoiceQuestion,
render: MultipleChoice,
args: {
framing:
'Three people are present when a pregnant person suddenly goes into labor and gives birth in a bank lobby.',
stem: 'Which of the people is likely to best remember the event afterward?',
choices: [
'Jayvon, who had opened a checking account at the branch that same day',
'Ibrahim, who is taking propranolol to control his blood pressure',
'Huong, who was born with Urbach-Wiethe syndrome and lacks an amygdala',
],
},
};

export default meta;
type Story = StoryObj<StoryProps>;

export const Example: Story = {};

export const ExampleWithInstructions: Story = {
args: {
attemptCount: 2,
},
};
43 changes: 0 additions & 43 deletions packages/react/src/patterns/MultipleChoice/PatternExample.tsx

This file was deleted.

2 changes: 2 additions & 0 deletions packages/react/src/patterns/MultipleChoice/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './useMultipleChoice';
export type * from './types';
Loading
Loading