Skip to content

Commit

Permalink
feat(LEMS-2722): Add scientific option to basic keypad (#1618)
Browse files Browse the repository at this point in the history
## Summary:
#### Proof of concept: 
Adds scientific notation to basic keypad using toggle on expression editor

Issue: LEMS-2272

## Test plan:
1. [Navigate to expression editor story](https://650db21c3f5d1b2f13c02952-evogyhpohm.chromatic.com/?path=/docs/perseuseditor-widgets-expression-editor--docs)
2. Toggle `scientific` in the editor to see the scientific notation added to the basic keypad.

#### Note
When adding other keypads, if scientific notation is toggled, the scientific notation button will remain visible. This includes when scientific and pre-algebra are toggled simultaneously, causing there to be 2 scientific notation buttons displayed on the screen. 
If we decide to pursue this further - it would be key to identify the use cases when we would display and hide the button, despite the toggle being set to `true` 

## Screenshots

https://github.com/user-attachments/assets/55d7395d-bd93-4aad-8195-b321be71e376

Author: anakaren-rojas

Reviewers: handeyeco, jeremywiebe, catandthemachines, anakaren-rojas

Required Reviewers:

Approved By: handeyeco, jeremywiebe, catandthemachines

Checks: ✅ codecov/project, ✅ codecov/patch, ✅ Upload Coverage (ubuntu-latest, 20.x), ✅ Publish npm snapshot (ubuntu-latest, 20.x), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ✅ Jest Coverage (ubuntu-latest, 20.x), ✅ Check builds for changes in size (ubuntu-latest, 20.x), ✅ Lint, Typecheck, Format, and Test (ubuntu-latest, 20.x), ✅ Cypress (ubuntu-latest, 20.x), ✅ Publish Storybook to Chromatic (ubuntu-latest, 20.x), ✅ gerald

Pull Request URL: #1618
  • Loading branch information
anakaren-rojas authored Oct 7, 2024
1 parent 22ee7bc commit 147f9a1
Show file tree
Hide file tree
Showing 14 changed files with 206 additions and 17 deletions.
7 changes: 7 additions & 0 deletions .changeset/twelve-rockets-taste.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@khanacademy/math-input": minor
"@khanacademy/perseus": minor
"@khanacademy/perseus-editor": minor
---

add scientific notation button / toggle to basic keypad
Original file line number Diff line number Diff line change
Expand Up @@ -329,4 +329,34 @@ describe("keypad", () => {
// Assert
expect(screen.getByTestId("period-decimal")).toBeInTheDocument();
});

it("shows the exponent button for scientific keypad", () => {
//Arrange
//Act
render(
<Keypad
onClickKey={() => {}}
scientific
onAnalyticsEvent={async () => {}}
/>,
);

//Assert
expect(
screen.getByRole("button", {name: "Custom exponent"}),
).toBeInTheDocument();
});

it("does not show the exponent button for non-scientific keypad", () => {
//Arrange
//Act
render(
<Keypad onClickKey={() => {}} onAnalyticsEvent={async () => {}} />,
);

//Assert
expect(
screen.queryByRole("button", {name: "Custom exponent"}),
).not.toBeInTheDocument();
});
});
3 changes: 3 additions & 0 deletions packages/math-input/src/components/keypad/keypad.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export type Props = {
basicRelations?: boolean;
advancedRelations?: boolean;
fractionsOnly?: boolean;
scientific?: boolean;

onClickKey: ClickKeyCallback;
onAnalyticsEvent: AnalyticsEventHandlerFn;
Expand Down Expand Up @@ -94,6 +95,7 @@ export default function Keypad(props: Props) {
logarithms,
basicRelations,
advancedRelations,
scientific,
showDismiss,
onAnalyticsEvent,
fractionsOnly,
Expand Down Expand Up @@ -187,6 +189,7 @@ export default function Keypad(props: Props) {
convertDotToTimes={convertDotToTimes}
divisionKey={divisionKey}
selectedPage={selectedPage}
scientific={scientific}
/>
)}
</View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ class MobileKeypadInternals
containerWidth > expandedViewThreshold
}
showDismiss
scientific={keypadConfig?.scientific}
/>
) : null}
</AphroditeCssTransitionGroup>
Expand Down
11 changes: 10 additions & 1 deletion packages/math-input/src/components/keypad/shared-keys.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type Props = {
cursorContext?: (typeof CursorContext)[keyof typeof CursorContext];
convertDotToTimes?: boolean;
divisionKey?: boolean;
scientific?: boolean;
};

export default function SharedKeys(props: Props) {
Expand All @@ -25,6 +26,7 @@ export default function SharedKeys(props: Props) {
divisionKey,
convertDotToTimes,
selectedPage,
scientific,
} = props;
const {strings, locale} = useMathInputI18n();
const cursorKeyConfig = getCursorContextConfig(strings, cursorContext);
Expand Down Expand Up @@ -56,7 +58,14 @@ export default function SharedKeys(props: Props) {
coord={[5, 0]}
secondary
/>

{scientific && (
<KeypadButton
keyConfig={Keys.EXP}
onClickKey={onClickKey}
coord={[3, 2]}
secondary
/>
)}
{/* Row 2 */}
<KeypadButton
keyConfig={
Expand Down
1 change: 1 addition & 0 deletions packages/math-input/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export type KeypadConfiguration = {
keypadType: KeypadType;
extraKeys?: ReadonlyArray<Key>;
times?: boolean;
scientific?: boolean;
};

export type KeyHandler = (key: Key) => Cursor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,23 @@ describe("expression-editor", () => {
});
});

it("should toggle scientific checkbox", async () => {
const onChangeMock = jest.fn();

render(<ExpressionEditor onChange={onChangeMock} />);
act(() => jest.runOnlyPendingTimers());

await userEvent.click(
screen.getByRole("checkbox", {
name: "scientific",
}),
);

expect(onChangeMock).toBeCalledWith({
buttonSets: ["basic", "scientific"],
});
});

it("should be possible to add an answer", async () => {
const onChangeMock = jest.fn();

Expand Down
1 change: 1 addition & 0 deletions packages/perseus-editor/src/widgets/expression-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const buttonSetsList: LegacyButtonSets = [
"basic",
"trig",
"prealgebra",
"scientific",
"logarithms",
"basic relations",
"advanced relations",
Expand Down
51 changes: 41 additions & 10 deletions packages/perseus/src/components/__tests__/math-input.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ import {testDependencies} from "../../../../../testing/test-dependencies";
import * as Dependencies from "../../dependencies";
import MathInput from "../math-input";

import type {KeypadButtonSets} from "../math-input";
import type {UserEvent} from "@testing-library/user-event";

const allButtonSets = {
const allButtonSets: KeypadButtonSets = {
advancedRelations: true,
basicRelations: true,
divisionKey: true,
logarithms: true,
preAlgebra: true,
trigonometry: true,
scientific: true,
};

describe("Perseus' MathInput", () => {
Expand All @@ -31,7 +33,7 @@ describe("Perseus' MathInput", () => {
});

it("renders", () => {
// Assemble
// Arrange
render(
<MathInput
onChange={() => {}}
Expand All @@ -50,7 +52,7 @@ describe("Perseus' MathInput", () => {
});

it("provides a default aria label", () => {
// Assemble
// Arrange
render(
<MathInput
onChange={() => {}}
Expand All @@ -69,7 +71,7 @@ describe("Perseus' MathInput", () => {
});

it("is possible to overwrite the aria label", () => {
// Assemble
// Arrange
render(
<MathInput
onChange={() => {}}
Expand All @@ -89,7 +91,7 @@ describe("Perseus' MathInput", () => {
});

it("is possible to type in the input", async () => {
// Assemble
// Arrange
const mockOnChange = jest.fn();
render(
<MathInput
Expand All @@ -114,7 +116,7 @@ describe("Perseus' MathInput", () => {
});

it("is possible to use buttons", async () => {
// Assemble
// Arrange
const mockOnChange = jest.fn();
render(
<MathInput
Expand Down Expand Up @@ -142,8 +144,37 @@ describe("Perseus' MathInput", () => {
expect(mockOnChange).toHaveBeenLastCalledWith("1+2-3");
});

it("is possible to use the scientific keypad", async () => {
// Arrange
const mockOnChange = jest.fn();
render(
<MathInput
onChange={mockOnChange}
keypadButtonSets={{scientific: true}}
analytics={{onAnalyticsEvent: () => Promise.resolve()}}
convertDotToTimes={false}
value=""
/>,
);
act(() => jest.runOnlyPendingTimers());

// Act
await userEvent.click(
screen.getByRole("button", {name: /open math keypad/}),
);
await userEvent.click(screen.getByRole("button", {name: "2"}));
await userEvent.click(
screen.getByRole("button", {name: "Custom exponent"}),
);
await userEvent.click(screen.getByRole("button", {name: "2"}));
act(() => jest.runOnlyPendingTimers());

// Assert
expect(mockOnChange).toHaveBeenLastCalledWith("2^{2}");
});

it("is possible to use buttons with legacy props", async () => {
// Assemble
// Arrange
const mockOnChange = jest.fn();
render(
<MathInput
Expand Down Expand Up @@ -173,7 +204,7 @@ describe("Perseus' MathInput", () => {
});

it("returns focus to input after button click", async () => {
// Assemble
// Arrange
render(
<MathInput
onChange={() => {}}
Expand All @@ -197,7 +228,7 @@ describe("Perseus' MathInput", () => {
});

it("does not return focus to input after button press via keyboard", async () => {
// Assemble
// Arrange
render(
<MathInput
onChange={() => {}}
Expand Down Expand Up @@ -226,7 +257,7 @@ describe("Perseus' MathInput", () => {
});

it("does not focus on the keypad button when it is clicked with the mouse", async () => {
// Assemble
// Arrange
render(
<MathInput
onChange={() => {}}
Expand Down
6 changes: 5 additions & 1 deletion packages/perseus/src/components/math-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@ import type {Keys, MathFieldInterface} from "@khanacademy/math-input";

type ButtonsVisibleType = "always" | "never" | "focused";

type KeypadButtonSets = {
export type KeypadButtonSets = {
advancedRelations?: boolean;
basicRelations?: boolean;
divisionKey?: boolean;
logarithms?: boolean;
preAlgebra?: boolean;
trigonometry?: boolean;
scientific?: boolean;
};

type Props = {
Expand Down Expand Up @@ -495,6 +496,9 @@ const mapButtonSets = (buttonSets?: LegacyButtonSets) => {
case "trig":
keypadButtonSets.trigonometry = true;
break;
case "scientific":
keypadButtonSets.scientific = true;
break;
case "basic":
default:
break;
Expand Down
2 changes: 1 addition & 1 deletion packages/perseus/src/perseus-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ export type LegacyButtonSets = ReadonlyArray<
| "basic"
| "basic+div"
| "trig"
| "scientific"
| "prealgebra"
| "logarithms"
| "basic relations"
Expand All @@ -406,7 +407,6 @@ export type LegacyButtonSets = ReadonlyArray<
export type PerseusExpressionWidgetOptions = {
// The expression forms the answer may come in
answerForms: ReadonlyArray<PerseusExpressionAnswerForm>;
// Different buttons sets that can show in the expression. Options are "basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations"
buttonSets: LegacyButtonSets;
// Variables that can be used as functions. Default: ["f", "g", "h"]
functions: ReadonlyArray<string>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export const DesktopKitchenSink = (args: StoryArgs): React.ReactElement => {
"logarithms",
"basic relations",
"advanced relations",
"scientific",
] as LegacyButtonSets,
};

Expand Down
Loading

0 comments on commit 147f9a1

Please sign in to comment.