Skip to content

Commit

Permalink
[Interactive Graph Editor] Add the ability to reorder locked figure s…
Browse files Browse the repository at this point in the history
…ettings (#1360)

## Summary:
Added some icon buttons so that content authors can reorder the locked figure
settings. This can be used to make sure that one figure can render on top
of another figure as desired. (The lower they are in the settings, the more
in the front they are on the graph.)

Note: I added more tests to the `interactive-graph-editor-locked-figures.test.tsx` file.
I think it's getting _too_ long, so I'm considering breaking it up into a different file per
`describe` block in the future in its own PR.

Issue: https://khanacademy.atlassian.net/browse/LEMS-1951

## Test plan:
`yarn jest packages/perseus-editor/src/widgets/__tests__/interactive-graph-editor-locked-figures.test.tsx`

Play around with the buttons in the storybook file, as demonstrated in the videos below.

http://localhost:6006/?path=/story/perseuseditor-editorpage--mafs-with-locked-figures-m-2-flag

## Videos

### Reordering in editor

https://github.com/Khan/perseus/assets/13231763/6bc92a9f-aa88-442d-bc47-7e10b3901354

### Updating order on graph

https://github.com/Khan/perseus/assets/13231763/05f9ce2a-28fc-4e30-9a12-77f8651a64f5

Author: nishasy

Reviewers: benchristel, mark-fitzgerald

Required Reviewers:

Approved By: benchristel

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

Pull Request URL: #1360
  • Loading branch information
nishasy authored Jun 20, 2024
1 parent a608098 commit 753d6ea
Show file tree
Hide file tree
Showing 21 changed files with 431 additions and 148 deletions.
6 changes: 6 additions & 0 deletions .changeset/neat-carrots-guess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@khanacademy/perseus": minor
"@khanacademy/perseus-editor": minor
---

[Interactive Graph Editor] Add the ability to reorder locked figure settings
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ export const Default = (args): React.ReactElement => {
return <LockedEllipseSettings {...args} />;
};

type StoryComponentType = StoryObj<typeof LockedEllipseSettings>;

// Set the default values in the control panel.
Default.args = {
const defaultProps = {
...getDefaultFigureForType("ellipse"),
onChangeProps: () => {},
onMove: () => {},
onRemove: () => {},
};

type StoryComponentType = StoryObj<typeof LockedEllipseSettings>;

// Set the default values in the control panel.
Default.args = defaultProps;

export const Controlled: StoryComponentType = {
render: function Render() {
const [props, setProps] = React.useState({
...getDefaultFigureForType("ellipse"),
onRemove: () => {},
});
const [props, setProps] = React.useState(defaultProps);

const handlePropsUpdate = (newProps) => {
setProps({
Expand Down Expand Up @@ -57,10 +57,7 @@ Controlled.parameters = {
export const Expanded: StoryComponentType = {
render: function Render() {
const [expanded, setExpanded] = React.useState(true);
const [props, setProps] = React.useState({
...getDefaultFigureForType("ellipse"),
onRemove: () => {},
});
const [props, setProps] = React.useState(defaultProps);

const handlePropsUpdate = (newProps) => {
setProps({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ export const Default = (args): React.ReactElement => {
return <LockedLineSettings {...args} />;
};

type StoryComponentType = StoryObj<typeof LockedLineSettings>;

// Set the default values in the control panel.
Default.args = {
const defaultProps = {
...getDefaultFigureForType("line"),
onChangeProps: () => {},
onMove: () => {},
onRemove: () => {},
};

type StoryComponentType = StoryObj<typeof LockedLineSettings>;

// Set the default values in the control panel.
Default.args = defaultProps;

export const Controlled: StoryComponentType = {
render: function Render() {
const [props, setProps] = React.useState({
...getDefaultFigureForType("line"),
onRemove: () => {},
});
const [props, setProps] = React.useState(defaultProps);

const handlePropsUpdate = (newProps) => {
setProps({
Expand Down Expand Up @@ -57,10 +57,7 @@ Controlled.parameters = {
*/
export const WithInvalidPoints: StoryComponentType = {
render: function Render() {
const [props, setProps] = React.useState({
...getDefaultFigureForType("line"),
onRemove: () => {},
});
const [props, setProps] = React.useState(defaultProps);

const handlePropsUpdate = (newProps) => {
setProps({
Expand All @@ -87,10 +84,7 @@ export const WithInvalidPoints: StoryComponentType = {
export const Expanded: StoryComponentType = {
render: function Render() {
const [expanded, setExpanded] = React.useState(true);
const [props, setProps] = React.useState({
...getDefaultFigureForType("line"),
onRemove: () => {},
});
const [props, setProps] = React.useState(defaultProps);

const handlePropsUpdate = (newProps) => {
setProps({
Expand All @@ -115,8 +109,7 @@ export const ExpandedNondefaultProps: StoryComponentType = {
render: function Render() {
const [expanded, setExpanded] = React.useState(true);
const [props, setProps] = React.useState({
...getDefaultFigureForType("line"),
onRemove: () => {},
...defaultProps,
kind: "segment" as const,
color: "green" as const,
lineStyle: "dashed" as const,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ export const Default = (args): React.ReactElement => {
return <LockedPointSettings {...args} />;
};

type StoryComponentType = StoryObj<typeof LockedPointSettings>;

// Set the default values in the control panel.
Default.args = {
const defaultProps = {
...getDefaultFigureForType("point"),
onChangeProps: () => {},
onMove: () => {},
onRemove: () => {},
};

type StoryComponentType = StoryObj<typeof LockedPointSettings>;

// Set the default values in the control panel.
Default.args = defaultProps;

export const Controlled: StoryComponentType = {
render: function Render() {
const [props, setProps] = React.useState({
...getDefaultFigureForType("point"),
onRemove: () => {},
});
const [props, setProps] = React.useState(defaultProps);

const handlePropsUpdate = (newProps) => {
setProps({
Expand All @@ -54,10 +54,7 @@ Controlled.parameters = {
export const Expanded: StoryComponentType = {
render: function Render() {
const [expanded, setExpanded] = React.useState(true);
const [props, setProps] = React.useState({
...getDefaultFigureForType("point"),
onRemove: () => {},
});
const [props, setProps] = React.useState(defaultProps);

const handlePropsUpdate = (newProps) => {
setProps({
Expand All @@ -81,12 +78,7 @@ export const Expanded: StoryComponentType = {
export const ExpandedNondefaultProps: StoryComponentType = {
render: function Render() {
const [expanded, setExpanded] = React.useState(true);
const [props, setProps] = React.useState({
...getDefaultFigureForType("point"),
onRemove: () => {},
color: "green" as const,
filled: false,
});
const [props, setProps] = React.useState(defaultProps);

const handlePropsUpdate = (newProps) => {
setProps({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ export const Default = (args): React.ReactElement => {
return <LockedPolygonSettings {...args} />;
};

type StoryComponentType = StoryObj<typeof LockedPolygonSettings>;

// Set the default values in the control panel.
Default.args = {
const defaultProps = {
...getDefaultFigureForType("polygon"),
onChangeProps: () => {},
onMove: () => {},
onRemove: () => {},
};

type StoryComponentType = StoryObj<typeof LockedPolygonSettings>;

// Set the default values in the control panel.
Default.args = defaultProps;

export const Controlled: StoryComponentType = {
render: function Render() {
const [props, setProps] = React.useState({
...getDefaultFigureForType("polygon"),
onRemove: () => {},
});
const [props, setProps] = React.useState(defaultProps);

const handlePropsUpdate = (newProps) => {
setProps({
Expand Down Expand Up @@ -57,10 +57,7 @@ Controlled.parameters = {
export const Expanded: StoryComponentType = {
render: function Render() {
const [expanded, setExpanded] = React.useState(true);
const [props, setProps] = React.useState({
...getDefaultFigureForType("polygon"),
onRemove: () => {},
});
const [props, setProps] = React.useState(defaultProps);

const handlePropsUpdate = (newProps) => {
setProps({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ export const Default = (args): React.ReactElement => {
return <LockedVectorSettings {...args} />;
};

type StoryComponentType = StoryObj<typeof LockedVectorSettings>;

// Set the default values in the control panel.
Default.args = {
const defaultProps = {
...getDefaultFigureForType("vector"),
onChangeProps: () => {},
onMove: () => {},
onRemove: () => {},
};

type StoryComponentType = StoryObj<typeof LockedVectorSettings>;

// Set the default values in the control panel.
Default.args = defaultProps;

export const Expanded: StoryComponentType = {
render: function Render() {
const [props, setProps] = React.useState({
...getDefaultFigureForType("vector"),
onRemove: () => {},
});
const [props, setProps] = React.useState(defaultProps);

const handlePropsUpdate = (newProps) => {
setProps({
Expand All @@ -54,10 +54,7 @@ export const Expanded: StoryComponentType = {
*/
export const WithInvalidPoints: StoryComponentType = {
render: function Render() {
const [props, setProps] = React.useState({
...getDefaultFigureForType("vector"),
onRemove: () => {},
});
const [props, setProps] = React.useState(defaultProps);

const handlePropsUpdate = (newProps) => {
setProps({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {UserEvent} from "@testing-library/user-event";
const defaultProps = {
...getDefaultFigureForType("ellipse"),
onChangeProps: () => {},
onMove: () => {},
onRemove: () => {},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {UserEvent} from "@testing-library/user-event";
const defaultProps = {
...getDefaultFigureForType("line"),
onChangeProps: () => {},
onMove: () => {},
onRemove: () => {},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {UserEvent} from "@testing-library/user-event";
const defaultProps = {
...getDefaultFigureForType("point"),
onRemove: () => {},
onMove: () => {},
onChangeProps: () => {},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {UserEvent} from "@testing-library/user-event";
const defaultProps = {
...getDefaultFigureForType("polygon"),
onChangeProps: () => {},
onMove: () => {},
onRemove: () => {},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {UserEvent} from "@testing-library/user-event";
const defaultProps = {
...getDefaultFigureForType("vector"),
onChangeProps: () => {},
onMove: () => {},
onRemove: () => {},
} as Props;

Expand Down
64 changes: 37 additions & 27 deletions packages/perseus-editor/src/components/defining-point-settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,41 @@ import CoordinatePairInput from "./coordinate-pair-input";
import LabeledSwitch from "./labeled-switch";
import LockedFigureSettingsAccordion from "./locked-figure-settings-accordion";

import type {AccordionProps} from "./locked-figure-settings";
import type {LockedPointType} from "@khanacademy/perseus";

export type Props = AccordionProps &
LockedPointType & {
/**
* Optional label for the point to display in the header summary.
* Defaults to "Point".
*/
label: string;
/**
* Whether the extra point settings are toggled open.
*/
showPoint?: boolean;
/**
* Optional error message to display.
*/
error?: string | null;
/**
* Called when the extra settings toggle switch is changed.
*/
onTogglePoint?: (newValue) => void;
/**
* Called when the props (coords, color, etc.) are updated.
*/
onChangeProps: (newProps: Partial<LockedPointType>) => void;
};
export type Props = LockedPointType & {
/**
* Optional label for the point to display in the header summary.
* Defaults to "Point".
*/
label: string;
/**
* Whether the extra point settings are toggled open.
*/
showPoint?: boolean;
/**
* Optional error message to display.
*/
error?: string | null;
/**
* Called when the extra settings toggle switch is changed.
*/
onTogglePoint?: (newValue) => void;
/**
* Called when the props (coords, color, etc.) are updated.
*/
onChangeProps: (newProps: Partial<LockedPointType>) => void;

// Accordion props
/**
* Whether this accordion is expanded.
*/
expanded?: boolean;
/**
* Called when the accordion is expanded or collapsed.
*/
onToggle?: (expanded: boolean) => void;
};

const DefiningPointSettings = (props: Props) => {
const {
Expand All @@ -55,6 +63,8 @@ const DefiningPointSettings = (props: Props) => {
error,
onChangeProps,
onTogglePoint,
expanded,
onToggle,
} = props;

function handleColorChange(newValue) {
Expand All @@ -63,8 +73,8 @@ const DefiningPointSettings = (props: Props) => {

return (
<LockedFigureSettingsAccordion
expanded={props.expanded}
onToggle={props.onToggle}
expanded={expanded}
onToggle={onToggle}
containerStyle={styles.container}
panelStyle={styles.accordionPanel}
header={
Expand Down
Loading

0 comments on commit 753d6ea

Please sign in to comment.