From acbc551400805083ef35e9abe7f2be2c56e80a57 Mon Sep 17 00:00:00 2001 From: Jeremy Wiebe Date: Fri, 25 Oct 2024 08:55:02 -0700 Subject: [PATCH 01/17] Modernize Perseus' components folder stories --- .../components/__stories__/graph.stories.tsx | 27 ++--- .../__stories__/graphie.stories.tsx | 29 ++--- .../components/__stories__/hud.stories.tsx | 38 ++----- .../components/__stories__/icon.stories.tsx | 27 ++--- .../__stories__/image-loader.stories.tsx | 74 +++++------- .../__stories__/info-tip.stories.tsx | 50 +++++---- .../__stories__/inline-icon.stories.tsx | 50 ++++----- .../input-with-examples.stories.tsx | 73 ++++++------ .../components/__stories__/lint.stories.tsx | 106 +++++++----------- .../__stories__/math-input.stories.tsx | 75 +++++++------ .../multi-button-group.stories.tsx | 86 ++++++-------- .../__stories__/number-input.stories.tsx | 61 ++++++---- .../__stories__/range-input.stories.tsx | 41 ++++--- .../simple-keypad-input.stories.tsx | 33 +++--- .../__stories__/sortable.stories.tsx | 94 ++++++---------- .../__stories__/stub-tag-editor.stories.tsx | 62 +++++----- .../__stories__/svg-image.stories.tsx | 85 ++++++-------- .../components/__stories__/tex.stories.tsx | 25 ++--- .../__stories__/text-input.stories.tsx | 44 ++++---- .../__stories__/text-list-editor.stories.tsx | 44 ++++---- .../__stories__/tooltip.stories.tsx | 69 +++++------- .../__stories__/zoomable-tex.stories.tsx | 52 ++++----- .../__stories__/zoomable.stories.tsx | 42 ++++--- packages/perseus/src/components/graph.tsx | 27 ++--- packages/perseus/src/components/hud.tsx | 4 + packages/perseus/src/components/icon.tsx | 61 +++++----- .../perseus/src/components/image-loader.tsx | 18 +-- .../perseus/src/components/inline-icon.tsx | 15 +-- .../src/components/input-with-examples.tsx | 2 - .../src/components/multi-button-group.tsx | 1 + .../perseus/src/components/number-input.tsx | 2 +- .../perseus/src/components/range-input.tsx | 43 +++---- .../src/components/simple-keypad-input.tsx | 31 +++-- .../src/components/text-list-editor.tsx | 2 +- packages/perseus/src/components/tooltip.tsx | 79 ++++++------- packages/perseus/src/icon-paths.ts | 6 +- 36 files changed, 712 insertions(+), 866 deletions(-) diff --git a/packages/perseus/src/components/__stories__/graph.stories.tsx b/packages/perseus/src/components/__stories__/graph.stories.tsx index 5215c417ce..eb9e28702a 100644 --- a/packages/perseus/src/components/__stories__/graph.stories.tsx +++ b/packages/perseus/src/components/__stories__/graph.stories.tsx @@ -1,27 +1,22 @@ -import * as React from "react"; - import Graph from "../graph"; import type {StoryObj, Meta} from "@storybook/react"; -type StoryArgs = StoryObj; - -type Story = Meta; +type Story = StoryObj; const size = 200; -export default { +const meta: Meta = { title: "Perseus/Components/Graph", -} as Story; - -export const SquareBoxSizeAndOtherwiseEmpty = ( - args: StoryArgs, -): React.ReactElement => { - return ; + component: Graph, + args: { + box: [size, size], + }, }; +export default meta; + +export const SquareBoxSizeAndOtherwiseEmpty: Story = {}; -export const LabeledSquaredBox = (args: StoryArgs): React.ReactElement => { - return ( - - ); +export const LabeledSquaredBox: Story = { + args: {labels: ["First label", "Second label"]}, }; diff --git a/packages/perseus/src/components/__stories__/graphie.stories.tsx b/packages/perseus/src/components/__stories__/graphie.stories.tsx index d0ee574c36..264f94f6c9 100644 --- a/packages/perseus/src/components/__stories__/graphie.stories.tsx +++ b/packages/perseus/src/components/__stories__/graphie.stories.tsx @@ -6,28 +6,23 @@ import Graphie from "../graphie"; import type {StoryObj, Meta} from "@storybook/react"; -type StoryArgs = StoryObj; - -type Story = Meta; +type Story = StoryObj; const size = 200; -export default { +const meta: Meta = { title: "Perseus/Components/Graphie", -} as Story; - -export const SquareBoxSizeAndOtherwiseEmpty = ( - args: StoryArgs, -): React.ReactElement => { - return ( - {}} - setup={() => {}} - /> - ); + component: Graphie, + args: { + box: [size, size], + setup: () => {}, + setDrawingAreaAvailable: () => {}, + }, }; +export default meta; + +export const SquareBoxSizeAndOtherwiseEmpty: Story = {}; -export const PieChartGraphieLabels = (args: StoryArgs): React.ReactElement => { +export const PieChartGraphieLabels = () => { return ; }; diff --git a/packages/perseus/src/components/__stories__/hud.stories.tsx b/packages/perseus/src/components/__stories__/hud.stories.tsx index cc3a402c76..9ab2812f25 100644 --- a/packages/perseus/src/components/__stories__/hud.stories.tsx +++ b/packages/perseus/src/components/__stories__/hud.stories.tsx @@ -1,35 +1,21 @@ -import * as React from "react"; +import {action} from "@storybook/addon-actions"; import Hud from "../hud"; import type {StoryObj, Meta} from "@storybook/react"; -type StoryArgs = StoryObj; +type Story = StoryObj; -type Story = Meta; - -export default { +const meta: Meta = { title: "Perseus/Components/HUD", -} as Story; - -export const TestMessageDisabled = (args: StoryArgs): React.ReactElement => { - return ( - {}} - /> - ); + component: Hud, + args: { + enabled: true, + fixedPosition: false, + message: "Test message", + onClick: action("onClick"), + }, }; +export default meta; -export const TestMessageEnabled = (args: StoryArgs): React.ReactElement => { - return ( - {}} - /> - ); -}; +export const Default: Story = {}; diff --git a/packages/perseus/src/components/__stories__/icon.stories.tsx b/packages/perseus/src/components/__stories__/icon.stories.tsx index 08cbd36e90..342a4a86bf 100644 --- a/packages/perseus/src/components/__stories__/icon.stories.tsx +++ b/packages/perseus/src/components/__stories__/icon.stories.tsx @@ -1,16 +1,13 @@ -import * as React from "react"; - import * as IconPaths from "../../icon-paths"; import IconComponent from "../icon"; import type {StoryObj, Meta} from "@storybook/react"; -type StoryArgs = StoryObj; - -type Story = Meta; +type Story = StoryObj; -export default { - title: "Perseus/Components", +const meta: Meta = { + title: "Perseus/Components/Icon", + component: IconComponent, args: { color: "#808", size: 25, @@ -27,12 +24,12 @@ export default { control: "select", }, }, -} as Story; +}; +export default meta; -export const Icon = (args: StoryArgs): React.ReactElement => ( - -); +export const Icon: Story = { + args: { + style: {display: "block"}, + icon: IconPaths.iconCheck, + }, +}; diff --git a/packages/perseus/src/components/__stories__/image-loader.stories.tsx b/packages/perseus/src/components/__stories__/image-loader.stories.tsx index 08abacefea..094cebdffb 100644 --- a/packages/perseus/src/components/__stories__/image-loader.stories.tsx +++ b/packages/perseus/src/components/__stories__/image-loader.stories.tsx @@ -1,60 +1,40 @@ -/* eslint-disable @khanacademy/ts-no-error-suppressions */ import * as React from "react"; -type StoryArgs = Record; - -type Story = { - title: string; -}; - import ImageLoader from "../image-loader"; +import type {Meta, StoryObj} from "@storybook/react"; + const svgUrl = "http://www.khanacademy.org/images/ohnoes-concerned.svg"; const imgUrl = "https://www.khanacademy.org/images/hand-tree.new.png"; -export default { +const meta: Meta = { title: "Perseus/Components/Image Loader", -} as Story; - -export const SvgImage = (args: StoryArgs): React.ReactElement => { - return ( - {}} - /> - ); + component: ImageLoader, + args: { + preloader: null, + imgProps: { + alt: "ALT", + }, + onUpdate: () => {}, + }, +}; +export default meta; + +type Story = StoryObj; + +export const SvgImage: Story = { + args: { + src: svgUrl, + }, }; -export const PngImage = (args: StoryArgs): React.ReactElement => { - return ( - {}} - /> - ); +export const PngImage: Story = { + args: {src: imgUrl}, }; -export const InvalidImageWithChildrenForFailedLoading = ( - args: StoryArgs, -): React.ReactElement => { - return ( - {}} - > - You can see me! The image failed to load. - - ); +export const InvalidImageWithChildrenForFailedLoading: Story = { + args: { + src: "http://abcdefiahofshiaof.noway.badimage.com", + children: You can see me! The image failed to load., + }, }; diff --git a/packages/perseus/src/components/__stories__/info-tip.stories.tsx b/packages/perseus/src/components/__stories__/info-tip.stories.tsx index 2e72878eb1..d4999a6a16 100644 --- a/packages/perseus/src/components/__stories__/info-tip.stories.tsx +++ b/packages/perseus/src/components/__stories__/info-tip.stories.tsx @@ -2,34 +2,40 @@ import * as React from "react"; import InfoTip from "../info-tip"; -type StoryArgs = Record; +import type {Meta, StoryObj} from "@storybook/react"; -type Story = { - title: string; +const meta: Meta = { + title: "Perseus/Components/Info Tip", + component: InfoTip, }; +export default meta; -export default { - title: "Perseus/Components/Info Tip", -} as Story; +type Story = StoryObj; -export const TextOnMouseover = (args: StoryArgs): React.ReactElement => { - return Sample text; +export const TextOnMouseover: Story = { + args: { + children: "Sample text", + }, }; -export const CodeInText = (args: StoryArgs): React.ReactElement => { - return ( - - Settings that you add here are available to the program as an object - returned by Program.settings() - - ); +export const CodeInText: Story = { + args: { + children: ( + <> + Settings that you add here are available to the program as an + object returned by Program.settings() + + ), + }, }; -export const MultipleElements = (args: StoryArgs): React.ReactElement => { - return ( - -

First paragraph

-

Second paragraph

-
- ); +export const MultipleElements: Story = { + args: { + children: ( + <> +

First paragraph

+

Second paragraph

+ + ), + }, }; diff --git a/packages/perseus/src/components/__stories__/inline-icon.stories.tsx b/packages/perseus/src/components/__stories__/inline-icon.stories.tsx index d9e0754a7b..3f4ef5e6e4 100644 --- a/packages/perseus/src/components/__stories__/inline-icon.stories.tsx +++ b/packages/perseus/src/components/__stories__/inline-icon.stories.tsx @@ -1,40 +1,30 @@ -import * as React from "react"; - import InlineIcon from "../inline-icon"; -type StoryArgs = Record; +import type {Meta, StoryObj} from "@storybook/react"; -type Story = { - title: string; +const meta: Meta = { + title: "Perseus/Components/Inline Icon", + component: InlineIcon, + args: { + path: "M62.808 49.728q0 3.36-2.352 5.88l-41.72 41.664q-2.352 2.408-5.768 2.408t-5.768-2.408l-4.872-4.76q-2.352-2.52-2.352-5.88t2.352-5.712l31.08-31.136-31.08-31.024q-2.352-2.52-2.352-5.88t2.352-5.712l4.872-4.76q2.296-2.408 5.768-2.408t5.768 2.408l41.72 41.664q2.352 2.296 2.352 5.656z", + height: 100, + width: 64, + }, }; +export default meta; -const defaultPath = { - path: "M62.808 49.728q0 3.36-2.352 5.88l-41.72 41.664q-2.352 2.408-5.768 2.408t-5.768-2.408l-4.872-4.76q-2.352-2.52-2.352-5.88t2.352-5.712l31.08-31.136-31.08-31.024q-2.352-2.52-2.352-5.88t2.352-5.712l4.872-4.76q2.296-2.408 5.768-2.408t5.768 2.408l41.72 41.664q2.352 2.296 2.352 5.656z", - height: 100, - width: 64, -} as const; +type Story = StoryObj; -export default { - title: "Perseus/Components/Inline Icon", -} as Story; - -export const BasicIconPathAndSizing = (args: StoryArgs): React.ReactElement => { - return ; -}; +export const BasicIconPathAndSizing: Story = {}; -export const BasicIconWithAdditionalStyling = ( - args: StoryArgs, -): React.ReactElement => { - return ( - - ); +export const BasicIconWithAdditionalStyling: Story = { + args: { + style: {color: "red"}, + }, }; -export const BasicIconWithAriaTitle = (args: StoryArgs): React.ReactElement => { - return ; +export const BasicIconWithAriaTitle: Story = { + args: { + title: "Sample ARIA title", + }, }; diff --git a/packages/perseus/src/components/__stories__/input-with-examples.stories.tsx b/packages/perseus/src/components/__stories__/input-with-examples.stories.tsx index 77d0222b2f..95583794fa 100644 --- a/packages/perseus/src/components/__stories__/input-with-examples.stories.tsx +++ b/packages/perseus/src/components/__stories__/input-with-examples.stories.tsx @@ -1,53 +1,48 @@ -import * as React from "react"; +import {actions} from "@storybook/addon-actions"; import InputWithExamples from "../input-with-examples"; -type StoryArgs = Record; +import type {Meta, StoryObj} from "@storybook/react"; -type Story = { - title: string; +const meta: Meta = { + title: "Perseus/Components/Input with Examples", + component: InputWithExamples, + args: { + examples: [], + id: "", + onChange: actions("onChange"), + value: "", + }, + argTypes: { + onChange: { + table: {disable: true}, + }, + }, }; +export default meta; + +type Story = StoryObj; -export default { - title: "Perseus/Components/Input with Examples", -} as Story; - -const defaultObject = { - examples: [], - id: "", - onChange: () => {}, - value: "", -} as const; const testExamples = ["Sample 1", "Sample 2", "Sample 3"]; -export const DefaultAndMostlyEmptyProps = ( - args: StoryArgs, -): React.ReactElement => { - return ; -}; +export const DefaultAndMostlyEmptyProps: Story = {}; -export const ListOfExamples = (args: StoryArgs): React.ReactElement => { - return ; +export const ListOfExamples: Story = { + args: { + examples: testExamples, + }, }; -export const AriaLabelTextWithListOfExamples = ( - args: StoryArgs, -): React.ReactElement => { - return ( - - ); +export const AriaLabelTextWithListOfExamples: Story = { + args: { + examples: testExamples, + labelText: "Test label", + }, }; -export const DisabledInput = (args: StoryArgs): React.ReactElement => { - return ( - - ); +export const DisabledInput = { + args: { + disabled: true, + examples: testExamples, + }, }; diff --git a/packages/perseus/src/components/__stories__/lint.stories.tsx b/packages/perseus/src/components/__stories__/lint.stories.tsx index e34d5a3140..8813554429 100644 --- a/packages/perseus/src/components/__stories__/lint.stories.tsx +++ b/packages/perseus/src/components/__stories__/lint.stories.tsx @@ -2,24 +2,9 @@ import * as React from "react"; import Lint from "../lint"; -import type {Meta} from "@storybook/react"; +import type {Meta, StoryObj} from "@storybook/react"; -const meta: Meta = { - title: "Perseus/Components/Lint", -}; - -export default meta; - -type StoryArgs = Record; - -const defaultObject = { - children:
This is the sample lint child
, - insideTable: false, - message: "Test message", - ruleName: "Test rule", -} as const; - -const Container = ({children}: {children: React.ReactNode}) => { +const Container = (Story) => { return (
{ border: "solid 1px grey", }} > - {children} +
); }; -export const DefaultLintContainerAndMessage = ( - args: StoryArgs, -): React.ReactElement => { - return ( - - - - ); -}; -export const LintSeverity1Error = (args: StoryArgs): React.ReactElement => { - return ( - - - - ); -}; -export const LintSeverity2Warning = (args: StoryArgs): React.ReactElement => { - return ( - - - - ); -}; -export const LintSeverity3Recommendation = ( - args: StoryArgs, -): React.ReactElement => { - return ( - - - - ); -}; -export const LintSeverity4OfflineReportingOnly = ( - args: StoryArgs, -): React.ReactElement => { - return ( - - - - ); +const meta: Meta = { + title: "Perseus/Components/Lint", + component: Lint, + decorators: [Container], + args: { + children:
This is the sample lint child
, + insideTable: false, + severity: 1, + message: "Test message", + ruleName: "Test rule", + }, + argTypes: { + children: {table: {disable: true}}, + severity: { + type: "number", + control: { + type: "range", + min: 1, + max: 4, + }, + }, + }, }; -export const InlineLintContainerAndMessage = ( - args: StoryArgs, -): React.ReactElement => { - return ( - - - - ); + +export default meta; + +type Story = StoryObj; + +export const DefaultLintContainerAndMessage: Story = {}; + +export const LintSeverity1Error: Story = {args: {severity: 1}}; +export const LintSeverity2Warning: Story = {args: {severity: 2}}; +export const LintSeverity3Recommendation: Story = {args: {severity: 3}}; +export const LintSeverity4OfflineReportingOnly: Story = {args: {severity: 4}}; +export const InlineLintContainerAndMessage: Story = { + args: { + inline: true, + }, }; diff --git a/packages/perseus/src/components/__stories__/math-input.stories.tsx b/packages/perseus/src/components/__stories__/math-input.stories.tsx index e59b4a4726..a009812a87 100644 --- a/packages/perseus/src/components/__stories__/math-input.stories.tsx +++ b/packages/perseus/src/components/__stories__/math-input.stories.tsx @@ -1,46 +1,53 @@ -import * as React from "react"; +import {actions} from "@storybook/addon-actions"; import MathInput from "../math-input"; -type StoryArgs = Record; +import type {Meta, StoryObj} from "@storybook/react"; -type Story = { - title: string; -}; - -export default { +const meta: Meta = { title: "Perseus/Components/Math Input", -} as Story; - -const defaultObject = { - keypadButtonSets: { - advancedRelations: true, - basicRelations: true, - divisionKey: true, - logarithms: true, - preAlgebra: true, - trigonometry: true, + component: MathInput, + args: { + keypadButtonSets: { + advancedRelations: true, + basicRelations: true, + divisionKey: true, + logarithms: true, + preAlgebra: true, + trigonometry: true, + }, + convertDotToTimes: false, + value: "", + onChange: actions("onChange"), + analytics: {onAnalyticsEvent: () => Promise.resolve()}, + labelText: "Math input", + }, + argTypes: { + onChange: { + table: {disable: true}, + }, + analytics: { + table: {disable: true}, + }, + }, + parameters: { + controls: {exclude: ["onChange", "analytics"]}, }, - convertDotToTimes: false, - value: "", - onChange: () => {}, - analytics: {onAnalyticsEvent: () => Promise.resolve()}, - labelText: "Math input", -} as const; - -export const DefaultWithBasicButtonSet = ( - args: StoryArgs, -): React.ReactElement => { - return ; }; -export const DefaultWithAriaLabel = (args: StoryArgs): React.ReactElement => { - return ; +export default meta; + +type Story = StoryObj; + +export const DefaultWithBasicButtonSet: Story = {}; + +export const DefaultWithAriaLabel: Story = { + args: {ariaLabel: "Sample label"}, }; -export const KeypadOpenByDefault = (args: StoryArgs): React.ReactElement => { - return ; +export const KeypadOpenByDefault: Story = { + args: {buttonsVisible: "always"}, }; -export const KeypadNeverVisible = (args: StoryArgs): React.ReactElement => { - return ; +export const KeypadNeverVisible: Story = { + args: {buttonsVisible: "never"}, }; diff --git a/packages/perseus/src/components/__stories__/multi-button-group.stories.tsx b/packages/perseus/src/components/__stories__/multi-button-group.stories.tsx index 72080bd6df..36305c37f5 100644 --- a/packages/perseus/src/components/__stories__/multi-button-group.stories.tsx +++ b/packages/perseus/src/components/__stories__/multi-button-group.stories.tsx @@ -2,65 +2,47 @@ import * as React from "react"; import MultiButtonGroup from "../multi-button-group"; -type StoryArgs = { - allowEmpty: boolean; -}; - -type Story = { - title: string; - args: StoryArgs; -}; +import type {PropsFor} from "@khanacademy/wonder-blocks-core"; +import type {Meta, StoryObj} from "@storybook/react"; -export default { +const meta: Meta = { title: "Perseus/Components/Muli-Button Group", + component: MultiButtonGroup, args: { allowEmpty: true, + buttons: [], + }, + render: function WithState(props: PropsFor) { + const [values, updateValues] = React.useState(props.values); + return ( + + ); }, -} as Story; - -const HarnassedButtonGroup = ( - props: Pick< - React.ComponentProps, - "buttons" | "allowEmpty" - >, -) => { - const [values, updateValues] = React.useState( - null as ReadonlyArray | null | undefined, - ); - - return ( - { - updateValues(newValues); - }} - /> - ); }; +export default meta; -export const ButtonsWithNoTitles = (args: StoryArgs): React.ReactElement => { - return ( - - ); +type Story = StoryObj; + +export const ButtonsWithNoTitles: Story = { + args: { + buttons: [ + {value: "One", content: "Item #1"}, + {value: "Two", content: "Item #2"}, + {value: "Three", content: "Item #3"}, + ], + }, }; -export const ButtonsWithTitles = (args: StoryArgs): React.ReactElement => { - return ( - - ); +export const ButtonsWithTitles: Story = { + args: { + buttons: [ + {value: "One", content: "Item #1", title: "The first item"}, + {value: "Two", content: "Item #2", title: "The second item"}, + {value: "Three", content: "Item #3", title: "The third item"}, + ], + }, }; diff --git a/packages/perseus/src/components/__stories__/number-input.stories.tsx b/packages/perseus/src/components/__stories__/number-input.stories.tsx index af0386a046..6c7757cdef 100644 --- a/packages/perseus/src/components/__stories__/number-input.stories.tsx +++ b/packages/perseus/src/components/__stories__/number-input.stories.tsx @@ -1,41 +1,54 @@ -import * as React from "react"; +import {action} from "@storybook/addon-actions"; import NumberInput from "../number-input"; -type StoryArgs = Record; +import type {Meta, StoryObj} from "@storybook/react"; -type Story = { - title: string; +const meta: Meta = { + title: "Perseus/Components/Number Input", + component: NumberInput, + args: { + onChange: action("onChange"), + onFormatChange: action("onFormatChange"), + }, + argTypes: { + onChange: {table: {disable: true}}, + onFormatChange: {table: {disable: true}}, + }, }; +export default meta; -const defaultObject = { - onChange: () => {}, -} as const; - -export default { - title: "Perseus/Components/Number Input", -} as Story; +type Story = StoryObj; -export const EmptyPropsObject = (args: StoryArgs): React.ReactElement => { - return ; -}; +export const EmptyPropsObject: Story = {}; -export const SampleValue = (args: StoryArgs): React.ReactElement => { - return ; +export const SampleValue: Story = { + args: {value: 1234567890}, }; -export const Placeholder = (args: StoryArgs): React.ReactElement => { - return ; +export const Placeholder: Story = { + args: { + placeholder: "Sample placeholder", + }, }; -export const SizeMini = (args: StoryArgs): React.ReactElement => { - return ; +export const SizeMini: Story = { + args: { + size: "mini", + placeholder: "Sample placeholder", + }, }; -export const SizeSmall = (args: StoryArgs): React.ReactElement => { - return ; +export const SizeSmall: Story = { + args: { + size: "small", + placeholder: "Sample placeholder", + }, }; -export const SizeNormal = (args: StoryArgs): React.ReactElement => { - return ; +export const SizeNormal: Story = { + args: { + size: "normal", + placeholder: "Sample placeholder", + }, }; diff --git a/packages/perseus/src/components/__stories__/range-input.stories.tsx b/packages/perseus/src/components/__stories__/range-input.stories.tsx index b25955dd37..de357c34a9 100644 --- a/packages/perseus/src/components/__stories__/range-input.stories.tsx +++ b/packages/perseus/src/components/__stories__/range-input.stories.tsx @@ -1,29 +1,34 @@ -import * as React from "react"; +import {action} from "@storybook/addon-actions"; import RangeInput from "../range-input"; -type StoryArgs = Record; +import type {Meta, StoryObj} from "@storybook/react"; -type Story = { - title: string; +const meta: Meta = { + title: "Perseus/Components/Range Input", + component: RangeInput, + args: { + value: [], + onChange: action("onChange"), + }, + argTypes: { + onChange: {table: {disable: true}}, + }, }; +export default meta; -export default { - title: "Perseus/Components/Range Input", -} as Story; +type Story = StoryObj; -export const EmptyValueArray = (args: StoryArgs): React.ReactElement => { - return {}} value={[]} />; -}; +export const EmptyValueArray: Story = {}; -export const SimpleWithSmallValueRanges = ( - args: StoryArgs, -): React.ReactElement => { - return {}} value={[-10, 10]} />; +export const SimpleWithSmallValueRanges: Story = { + args: { + value: [-10, 10], + }, }; -export const Placeholders = (args: StoryArgs): React.ReactElement => { - return ( - {}} placeholder={["?", "!"]} value={[]} /> - ); +export const Placeholders: Story = { + args: { + placeholder: ["?", "!"], + }, }; diff --git a/packages/perseus/src/components/__stories__/simple-keypad-input.stories.tsx b/packages/perseus/src/components/__stories__/simple-keypad-input.stories.tsx index 53dfe44bc3..b642427853 100644 --- a/packages/perseus/src/components/__stories__/simple-keypad-input.stories.tsx +++ b/packages/perseus/src/components/__stories__/simple-keypad-input.stories.tsx @@ -1,27 +1,24 @@ -import * as React from "react"; +import {action} from "@storybook/addon-actions"; import SimpleKeypadInput from "../simple-keypad-input"; -type StoryArgs = Record; +import type {Meta, StoryObj} from "@storybook/react"; -type Story = { - title: string; +const meta: Meta = { + title: "Perseus/Components/Simple Keypad Input", + component: SimpleKeypadInput, + args: { + onChange: action("onChange"), + onFocus: action("onFocus"), + onBlur: action("onBlur"), + }, }; +export default meta; -const defaultObject = { - onChange: () => {}, - onFocus: () => {}, - onBlur: () => {}, -} as const; - -export default { - title: "Perseus/Components/Simple Keypad Input", -} as Story; +type Story = StoryObj; -export const EmptyPropsObject = (args: StoryArgs): React.ReactElement => { - return ; -}; +export const EmptyPropsObject: Story = {}; -export const CustomValue = (args: StoryArgs): React.ReactElement => { - return ; +export const CustomValue: Story = { + args: {value: "Test value"}, }; diff --git a/packages/perseus/src/components/__stories__/sortable.stories.tsx b/packages/perseus/src/components/__stories__/sortable.stories.tsx index 9821b822b0..d0b5723446 100644 --- a/packages/perseus/src/components/__stories__/sortable.stories.tsx +++ b/packages/perseus/src/components/__stories__/sortable.stories.tsx @@ -1,77 +1,53 @@ -import * as React from "react"; - import Sortable from "../sortable"; -type StoryArgs = Record; +import type {Meta, StoryObj} from "@storybook/react"; -type Story = { - title: string; +const meta: Meta = { + title: "Perseus/Components/Sortable", + component: Sortable, + args: { + options: ["Option 1", "Option 2", "Option 3"], + }, }; +export default meta; -const defaultOptions = ["Option 1", "Option 2", "Option 3"]; +type Story = StoryObj; -export default { - title: "Perseus/Components/Sortable", -} as Story; - -export const SortableHorizontalExample = ( - args: StoryArgs, -): React.ReactElement => { - return ( - - ); +export const SortableHorizontalExample: Story = { + args: { + layout: "horizontal", + options: ["a", "b", "c"], + waitForTexRendererToLoad: false, + }, }; -export const SortableVerticalExample = ( - args: StoryArgs, -): React.ReactElement => { - return ( - - ); +export const SortableVerticalExample: Story = { + args: { + layout: "vertical", + options: ["a", "b", "c"], + waitForTexRendererToLoad: false, + }, }; -export const BasicSortableOptionsTest = ( - args: StoryArgs, -): React.ReactElement => { - return ; -}; +export const BasicSortableOptionsTest: Story = {}; -export const BasicSortableOptionsTestWithNoPadding = ( - args: StoryArgs, -): React.ReactElement => { - return ; +export const BasicSortableOptionsTestWithNoPadding: Story = { + args: {padding: false}, }; -export const BasicSortableOptionsTestWithLargeMargin = ( - args: StoryArgs, -): React.ReactElement => { - return ; +export const BasicSortableOptionsTestWithLargeMargin: Story = { + args: {margin: 64}, }; -export const BasicSortableOptionsTestDisabled = ( - args: StoryArgs, -): React.ReactElement => { - return ; +export const BasicSortableOptionsTestDisabled: Story = { + args: {disabled: true}, }; -export const BasicSortableOptionsTestWithWidthAndHeightConstraints = ( - args: StoryArgs, -): React.ReactElement => { - return ( - - ); +export const BasicSortableOptionsTestWithWidthAndHeightConstraints: Story = { + args: { + constraints: { + height: 128, + width: 256, + }, + }, }; diff --git a/packages/perseus/src/components/__stories__/stub-tag-editor.stories.tsx b/packages/perseus/src/components/__stories__/stub-tag-editor.stories.tsx index fa0144c5e8..2480fab74a 100644 --- a/packages/perseus/src/components/__stories__/stub-tag-editor.stories.tsx +++ b/packages/perseus/src/components/__stories__/stub-tag-editor.stories.tsx @@ -1,45 +1,47 @@ -import * as React from "react"; +import {actions} from "@storybook/addon-actions"; import StubTagEditor from "../stub-tag-editor"; -type StoryArgs = Record; - -type Story = { - title: string; +import type {Meta, StoryObj} from "@storybook/react"; + +const meta: Meta = { + title: "Perseus/Components/Stub Tag Editor", + component: StubTagEditor, + args: { + value: [], + onChange: actions("onChange"), + }, + argTypes: { + onChange: { + table: {disable: true}, + }, + }, }; -export default { - title: "Perseus/Components/name", -} as Story; +export default meta; + +type Story = StoryObj; const defaultValues = ["Test value 1", "Test value 2", "Test value 3"]; -export const ShowingTitle = (args: StoryArgs): React.ReactElement => { - return {}} showTitle={true} />; +export const ShowingTitle: Story = { + args: {showTitle: true}, }; -export const NotShowingTitle = (args: StoryArgs): React.ReactElement => { - return {}} showTitle={false} />; +export const NotShowingTitle: Story = { + args: {showTitle: false}, }; -export const ShowingTitleWithValue = (args: StoryArgs): React.ReactElement => { - return ( - {}} - showTitle={true} - value={defaultValues} - /> - ); +export const ShowingTitleWithValue: Story = { + args: { + showTitle: true, + value: defaultValues, + }, }; -export const NotShowingTitleWithValue = ( - args: StoryArgs, -): React.ReactElement => { - return ( - {}} - showTitle={false} - value={defaultValues} - /> - ); +export const NotShowingTitleWithValue: Story = { + args: { + showTitle: false, + value: defaultValues, + }, }; diff --git a/packages/perseus/src/components/__stories__/svg-image.stories.tsx b/packages/perseus/src/components/__stories__/svg-image.stories.tsx index 7bacd30ddb..a9041ecb74 100644 --- a/packages/perseus/src/components/__stories__/svg-image.stories.tsx +++ b/packages/perseus/src/components/__stories__/svg-image.stories.tsx @@ -1,74 +1,61 @@ -import * as React from "react"; - import SvgImage from "../svg-image"; -type StoryArgs = Record; +import type {Meta, StoryObj} from "@storybook/react"; -type Story = { - title: string; +const meta: Meta = { + title: "Perseus/Components/SVG Image", + component: SvgImage, + args: {alt: "ALT"}, }; +export default meta; -export default { - title: "Perseus/Components/SVG Image", -} as Story; +type Story = StoryObj; const svgUrl = "http://www.khanacademy.org/images/ohnoes-concerned.svg"; const imgUrl = "https://www.khanacademy.org/images/hand-tree.new.png"; const graphieUrl = "web+graphie://ka-perseus-graphie.s3.amazonaws.com/1e06f6d4071f30cee2cc3ccb7435b3a66a62fe3f"; -export const MostlyEmptyPropsObject = (args: StoryArgs): React.ReactElement => { - return ; -}; +export const Default: Story = {}; -export const SvgImageThatDoesntLoad = (args: StoryArgs): React.ReactElement => { - return ( - - ); +export const SvgImageThatDoesntLoad: Story = { + args: { + height: 100, + width: 500, + src: "http://httpstat.us/200?sleep=1000000", + }, }; -export const SvgImageBasic = (args: StoryArgs): React.ReactElement => { - return ; +export const SvgImageBasic: Story = { + args: {src: svgUrl}, }; -export const SvgImageWithFixedHeight = ( - args: StoryArgs, -): React.ReactElement => { - return ; +export const SvgImageWithFixedHeight: Story = { + args: {height: 50, src: svgUrl}, }; -export const SvgImageWithFixedWidth = (args: StoryArgs): React.ReactElement => { - return ; +export const SvgImageWithFixedWidth: Story = { + args: {src: svgUrl, width: 50}, }; -export const SvgImageWithExtraGraphieProps = ( - args: StoryArgs, -): React.ReactElement => { - return ( - - ); +export const SvgImageWithExtraGraphieProps: Story = { + args: { + extraGraphie: { + box: [200, 200], + range: [ + [0, 10], + [0, 10], + ], + labels: ["ok"], + }, + src: svgUrl, + }, }; -export const PngImage = (args: StoryArgs): React.ReactElement => { - return ; +export const PngImage: Story = { + args: {src: imgUrl}, }; -export const GraphieImage = (args: StoryArgs): React.ReactElement => { - return ; +export const GraphieImage: Story = { + args: {src: graphieUrl}, }; diff --git a/packages/perseus/src/components/__stories__/tex.stories.tsx b/packages/perseus/src/components/__stories__/tex.stories.tsx index 2f65e3ab21..04d2a4cb12 100644 --- a/packages/perseus/src/components/__stories__/tex.stories.tsx +++ b/packages/perseus/src/components/__stories__/tex.stories.tsx @@ -1,23 +1,16 @@ -import * as React from "react"; - import TeX from "../tex"; -type StoryArgs = { - equation: string; -}; - -type Story = { - title: string; - args: StoryArgs; -}; +import type {Meta, StoryObj} from "@storybook/react"; -export default { +const meta: Meta = { title: "Perseus/Components/Tex", + component: TeX, args: { - equation: "f(x) = x + 1", + children: "f(x) = x + 1", }, -} as Story; - -export const BasicOperation = (args: StoryArgs): React.ReactElement => { - return {}} children={args.equation} />; }; +export default meta; + +type Story = StoryObj; + +export const BasicOperation: Story = {}; diff --git a/packages/perseus/src/components/__stories__/text-input.stories.tsx b/packages/perseus/src/components/__stories__/text-input.stories.tsx index 33e15e390d..ec687be8e6 100644 --- a/packages/perseus/src/components/__stories__/text-input.stories.tsx +++ b/packages/perseus/src/components/__stories__/text-input.stories.tsx @@ -1,33 +1,37 @@ -import * as React from "react"; +import {actions} from "@storybook/addon-actions"; import TextInput from "../text-input"; -type StoryArgs = Record; +import type {Meta, StoryObj} from "@storybook/react"; -type Story = { - title: string; -}; - -export default { +const meta: Meta = { title: "Perseus/Components/Text Input", -} as Story; + component: TextInput, + args: { + onChange: actions("onChange"), + onBlur: actions("onBlur"), + onFocus: actions("onFocus"), + }, + argTypes: { + onChange: {table: {disable: true}}, + onBlur: {table: {disable: true}}, + onFocus: {table: {disable: true}}, + }, +}; +export default meta; -const defaultObject = { - onChange: () => {}, -} as const; +type Story = StoryObj; -export const EmptyPropsObject = (args: StoryArgs): React.ReactElement => { - return ; -}; +export const EmptyPropsObject: Story = {}; -export const TestValueProvided = (args: StoryArgs): React.ReactElement => { - return ; +export const TestValueProvided: Story = { + args: {value: "Test value"}, }; -export const AriaLabelTextProvided = (args: StoryArgs): React.ReactElement => { - return ; +export const AriaLabelTextProvided: Story = { + args: {labelText: "Test label"}, }; -export const Disabled = (args: StoryArgs): React.ReactElement => { - return ; +export const Disabled: Story = { + args: {disabled: true}, }; diff --git a/packages/perseus/src/components/__stories__/text-list-editor.stories.tsx b/packages/perseus/src/components/__stories__/text-list-editor.stories.tsx index c67e99da76..568ffabb11 100644 --- a/packages/perseus/src/components/__stories__/text-list-editor.stories.tsx +++ b/packages/perseus/src/components/__stories__/text-list-editor.stories.tsx @@ -1,32 +1,30 @@ -import {action} from "@storybook/addon-actions"; +import {actions} from "@storybook/addon-actions"; import * as React from "react"; import TextListEditor from "../text-list-editor"; -type StoryArgs = Record; +import type {Meta, StoryObj} from "@storybook/react"; -type Story = { - title: string; -}; - -export default { +const meta: Meta = { title: "Perseus/Components/Text List Editor", -} as Story; - -const defaultObject = { - onChange: (...args) => { - action("onChange")(...args); + component: TextListEditor, + args: { + options: ["Test option 1", "Test option 2", "Test option 3"], + onChange: actions("onChange"), }, - options: ["Test option 1", "Test option 2", "Test option 3"], -} as const; + argTypes: { + onChange: {table: {disable: true}}, + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +}; +export default meta; -const ClassName = "framework-perseus orderer"; +type Story = StoryObj; -export const SimpleListOfOptions = (args: StoryArgs): React.ReactElement => { - return ( - // @ts-expect-error [FEI-5003] - TS2322 - Type '{ children: Element; class: string; }' is not assignable to type 'DetailedHTMLProps, HTMLDivElement>'. -
- -
- ); -}; +export const SimpleListOfOptions: Story = {}; diff --git a/packages/perseus/src/components/__stories__/tooltip.stories.tsx b/packages/perseus/src/components/__stories__/tooltip.stories.tsx index 7a63fa5199..52dacf9dd8 100644 --- a/packages/perseus/src/components/__stories__/tooltip.stories.tsx +++ b/packages/perseus/src/components/__stories__/tooltip.stories.tsx @@ -3,50 +3,39 @@ import * as React from "react"; import Tooltip, {HorizontalDirection, VerticalDirection} from "../tooltip"; -import type {Meta} from "@storybook/react"; +import type {Meta, StoryObj} from "@storybook/react"; -const meta: Meta = { +const meta: Meta = { title: "Perseus/Components/Tooltip", + component: Tooltip, + render(props) { + return ( + + Hover over{" "} + + this + + You can read so much more if you want... + + {" "} + to see more information + + ); + }, }; +export default meta; -export const Shown = () => { - return ( - - Hover over{" "} - - this - - You can read so much more if you want... - - {" "} - to see more information - - ); -}; +type Story = StoryObj; -export const Hidden = () => { - return ( - - Hover over{" "} - - this - - You can read so much more if you want... - - {" "} - to see more information - - ); +export const Shown: Story = { + args: {show: true}, }; -export default meta; +export const Hidden: Story = { + args: {show: false}, +}; diff --git a/packages/perseus/src/components/__stories__/zoomable-tex.stories.tsx b/packages/perseus/src/components/__stories__/zoomable-tex.stories.tsx index 1992575501..b68a9b18fa 100644 --- a/packages/perseus/src/components/__stories__/zoomable-tex.stories.tsx +++ b/packages/perseus/src/components/__stories__/zoomable-tex.stories.tsx @@ -2,40 +2,34 @@ import * as React from "react"; import ZoomableTex from "../zoomable-tex"; -type StoryArgs = Record; +import type {Meta, StoryObj} from "@storybook/react"; -type Story = { - title: string; -}; - -export default { +const meta: Meta = { title: "Perseus/Components/Zoomable Tex", -} as Story; - -type Props = { - children: React.ReactNode; + component: ZoomableTex, + decorators: [ + function ForceZoomWrapper(Story) { + return ( + <> +

Click on equation to zoom/unzoom

+
+ +
+ + ); + }, + ], }; +export default meta; -const ForceZoomWrapper = ({children}: Props): React.ReactElement => ( - <> -

Click on equation to zoom/unzoom

-
{children}
- -); +type Story = StoryObj; -export const Tex = (args: StoryArgs): React.ReactElement => { - return ( - - - - ); +export const Tex: Story = { + args: {children: "\\sum_{i=1}^\\infty\\frac{1}{n^2} = \\frac{\\pi^2}{6}"}, }; -export const ComplexTex = (args: StoryArgs): React.ReactElement => { - return ( - - {" "} - - - ); +export const ComplexTex: Story = { + args: { + children: `\\begin{aligned}h\\blueE{v_1} \\left(\\dfrac{\\partial f}{\\partial x}(x_0, y_0) \\right) + h\\greenE{v_2}\\left( \\dfrac{\\partial f}{\\partial y}(x_0 \\redD{+ h\\blueE{v_1}}, y_0)\\right)\\end{aligned}`, + }, }; diff --git a/packages/perseus/src/components/__stories__/zoomable.stories.tsx b/packages/perseus/src/components/__stories__/zoomable.stories.tsx index 6dc18fa61d..80fd1984f5 100644 --- a/packages/perseus/src/components/__stories__/zoomable.stories.tsx +++ b/packages/perseus/src/components/__stories__/zoomable.stories.tsx @@ -2,20 +2,7 @@ import * as React from "react"; import Zoomable from "../zoomable"; -type StoryArgs = Record; - -type Story = { - title: string; -}; - -export default { - title: "Perseus/Components/Zoomable", - argTypes: { - disableEntranceAnimation: { - control: {type: "boolean"}, - }, - }, -} as Story; +import type {Meta, StoryObj} from "@storybook/react"; type Bounds = { width: number; @@ -32,18 +19,29 @@ const computeChildBounds = ( }; }; -export const ZoomableExample = (args: StoryArgs): React.ReactElement => { - return ( - +const meta: Meta = { + title: "Perseus/Components/Zoomable", + component: Zoomable, + args: { + computeChildBounds, + }, + argTypes: { + children: {table: {disable: true}}, + }, +}; +export default meta; + +type Story = StoryObj; + +export const ZoomableExample: Story = { + args: { + children: ( Here's some zoomed-out content.

Click on the content to zoom/unzoom.
-
- ); + ), + }, }; diff --git a/packages/perseus/src/components/graph.tsx b/packages/perseus/src/components/graph.tsx index e8e8af9210..08674fa5ff 100644 --- a/packages/perseus/src/components/graph.tsx +++ b/packages/perseus/src/components/graph.tsx @@ -3,7 +3,6 @@ import {vector as kvector} from "@khanacademy/kmath"; import $ from "jquery"; import * as React from "react"; -import ReactDOM from "react-dom"; import _ from "underscore"; import AssetContext from "../asset-context"; @@ -86,10 +85,9 @@ class Graph extends React.Component { protractor: any; ruler: any; _graphie: any; - // @ts-expect-error - TS2564 - Property '_hasSetupGraphieThisUpdate' has no initializer and is not definitely assigned in the constructor. - _hasSetupGraphieThisUpdate: boolean; - // @ts-expect-error - TS2564 - Property '_shouldSetupGraphie' has no initializer and is not definitely assigned in the constructor. - _shouldSetupGraphie: boolean; + _hasSetupGraphieThisUpdate = false; + _shouldSetupGraphie = true; + graphieDiv = React.createRef(); static defaultProps: DefaultProps = { labels: ["x", "y"], @@ -186,11 +184,11 @@ class Graph extends React.Component { if (this._hasSetupGraphieThisUpdate) { return; } + if (this.graphieDiv.current == null) { + return; + } - // eslint-disable-next-line react/no-string-refs - const graphieDiv = ReactDOM.findDOMNode(this.refs.graphieDiv); - // @ts-expect-error - TS2769 - No overload matches this call. | TS2339 - Property 'empty' does not exist on type 'JQueryStatic'. - $(graphieDiv).empty(); + $(this.graphieDiv.current).empty(); // Content creators may need to explicitly add the dollar signs so the // strings are picked up by our translation tools. However, these math @@ -202,8 +200,9 @@ class Graph extends React.Component { Util.unescapeMathMode(label), ); const range = this.props.range; - // @ts-expect-error - TS2345: Argument of type 'Element | Text | null' is not assignable to parameter of type 'HTMLElement'. - const graphie = (this._graphie = GraphUtils.createGraphie(graphieDiv)); + const graphie = (this._graphie = GraphUtils.createGraphie( + this.graphieDiv.current, + )); const gridConfig: [GridDimensions, GridDimensions] = this._getGridConfig(); @@ -271,8 +270,7 @@ class Graph extends React.Component { }); $instructionsWrapper.append($instructions); - // @ts-expect-error - TS2769 - No overload matches this call. | TS2339 - Property 'append' does not exist on type 'JQueryStatic'. - $(graphieDiv).append($instructionsWrapper); + $(this.graphieDiv.current).append($instructionsWrapper); } else { $instructionsWrapper = undefined; } @@ -431,8 +429,7 @@ class Graph extends React.Component { onClick={this.onClick} > {image} - {/* eslint-disable-next-line react/no-string-refs */} -
+
); } diff --git a/packages/perseus/src/components/hud.tsx b/packages/perseus/src/components/hud.tsx index 03cb916531..6f7cb7550b 100644 --- a/packages/perseus/src/components/hud.tsx +++ b/packages/perseus/src/components/hud.tsx @@ -84,6 +84,10 @@ type Props = { fixedPosition?: boolean; }; +/** + * A "heads-up display" (HUD) indicator that includes a short message (usually + * used for linting errors). The indicator can be disabled. + */ const HUD = ({message, enabled, onClick, fixedPosition = true}: Props) => { let state; let icon; diff --git a/packages/perseus/src/components/icon.tsx b/packages/perseus/src/components/icon.tsx index db4097f88c..fabb1e70d5 100644 --- a/packages/perseus/src/components/icon.tsx +++ b/packages/perseus/src/components/icon.tsx @@ -1,34 +1,4 @@ /* eslint-disable @khanacademy/ts-no-error-suppressions */ -/** - * SVG Icon React Component - * - * This component is designed to take in SVG paths and render icons based upon - * them. If you are looking for an icon that we've used before you should look - * in `icon-paths.js` which is a reference file for all the SVG paths that - * we've used. You'll need to copy the object from that file into whichever - * file you're using the icon and explicitly pass it in to the React - * component. - * - * Sample usage: - * - * const dropdownIcon = `M5,6L0,0L10,0`; - * - * - * Or: - * - * const dropdownIcon = { - * path: `M5,6L0,0L10,0`, - * height: 10, - * width: 10, - * }; - * - * - * A full list of all the existing icons can be seen here: - * http://localhost:8080/react-sandbox/shared-styles-package/icon.jsx.fixture.js - * - * If you want to add an entirely new icon please read the note inside - * the `icon-paths.js` file. - */ import * as React from "react"; @@ -78,6 +48,37 @@ type Props = { alt?: string; }; +/** + * An SVG Icon + * + * This component is designed to take in SVG paths and render icons based upon + * them. If you are looking for an icon that we've used before you should look + * in `icon-paths.js` which is a reference file for all the SVG paths that + * we've used. You'll need to copy the object from that file into whichever + * file you're using the icon and explicitly pass it in to the React + * component. + * + * Sample usage: + * + * ``` + * const dropdownIcon = `M5,6L0,0L10,0`; + * + * ``` + * + * Or: + * + * ``` + * const dropdownIcon = { + * path: `M5,6L0,0L10,0`, + * height: 10, + * width: 10, + * }; + * + * ``` + * + * If you want to add an entirely new icon please read the note inside + * the `icon-paths.ts` file. + */ class Icon extends React.Component { static defaultProps: { color: string; diff --git a/packages/perseus/src/components/image-loader.tsx b/packages/perseus/src/components/image-loader.tsx index 18f7de644b..3baf5615ec 100644 --- a/packages/perseus/src/components/image-loader.tsx +++ b/packages/perseus/src/components/image-loader.tsx @@ -1,15 +1,6 @@ /* eslint-disable @khanacademy/ts-no-error-suppressions */ /* eslint-disable jsx-a11y/alt-text, react/no-unsafe */ // TODO(scottgrant): Enable the alt-text eslint rule above. -/** - * Component to display an image (or other React components) while the desired - * image is loading. - * - * Derived from - * https://github.com/hzdg/react-imageloader/blob/master/src/index.js - * to better suit our environment/build tools. Additionally, this one does - * not introduce a wrapper element, which makes styling easier. - */ import * as React from "react"; @@ -49,6 +40,15 @@ type State = { status: (typeof Status)[keyof typeof Status]; }; +/** + * Component to display an image (or other React components) while the desired + * image is loading. + * + * Derived from + * https://github.com/hzdg/react-imageloader/blob/master/src/index.js + * to better suit our environment/build tools. Additionally, this one does + * not introduce a wrapper element, which makes styling easier. + */ class ImageLoader extends React.Component { img: HTMLImageElement | null | undefined; diff --git a/packages/perseus/src/components/inline-icon.tsx b/packages/perseus/src/components/inline-icon.tsx index 20087309fa..9671911b23 100644 --- a/packages/perseus/src/components/inline-icon.tsx +++ b/packages/perseus/src/components/inline-icon.tsx @@ -17,11 +17,7 @@ type InlineIconProps = { * A stripped version of Icon.jsx from webapp. Takes an SVG icon and renders it * inline like Font Awesome did. * - * If you are looking for an icon that we've used before you should look in - * webapp's `icon-paths.js` which is a reference file for all the SVG paths - * that we've used. You'll need to copy the object from that file into - * whichever file you're using the icon and explicitly pass it in to the - * React component. + * You can refer to `icon-paths.ts` for all available SVG icons. * * We assume that the viewBox is cropped and aligned to (0, 0), but icons can * be defined differently. At some point we might want to add these attributes @@ -29,13 +25,14 @@ type InlineIconProps = { * * Sample usage: * - * const editIcon = { + * ``` + * const editIcon = { * path: "M41.209 53.753l5.39 0l0 5.39l3.136 0l6.468-6.517-8.477-8.526-6.517 6.517l0 3.136zm33.173-34.937q-.882-.882-1.862.049l-19.6 19.6q-.931.98-.049 1.862t1.862-.049l19.6-19.6q.931-.98.049-1.862zm-38.563 45.668l0-16.121l37.632-37.632 16.17 16.121-37.632 37.632l-16.17 0zm43.022-12.397l0 10.633q-.049 6.713-4.753 11.417t-11.368 4.704l-46.599 0q-6.713 0-11.417-4.753t-4.704-11.368l0-46.599q0-6.664 4.753-11.417t11.368-4.704l46.599 0q3.528 0 6.566 1.372.833.392.98 1.323t-.49 1.617l-2.744 2.744q-.784.784-1.96.441t-2.352-.343l-46.599 0q-3.675 0-6.321 2.646t-2.646 6.321l0 46.599q0 3.675 2.646 6.321t6.321 2.646l46.599 0q3.675 0 6.321-2.646t2.646-6.321l0-7.056q0-.735.49-1.225l3.577-3.577q.833-.833 1.96-.392t1.127 1.617zm7.203-51.646q2.254 0 3.773 1.568l8.526 8.526q1.568 1.568 1.568 3.822t-1.568 3.773l-5.145 5.145-16.121-16.121 5.145-5.145q1.568-1.568 3.822-1.568z", * width: 100, * height: 78.912, - * }; - * - * + * }; + * + * ``` */ const InlineIcon = ({ path, diff --git a/packages/perseus/src/components/input-with-examples.tsx b/packages/perseus/src/components/input-with-examples.tsx index 2bc915afb7..da253dfec3 100644 --- a/packages/perseus/src/components/input-with-examples.tsx +++ b/packages/perseus/src/components/input-with-examples.tsx @@ -155,8 +155,6 @@ class InputWithExamples extends React.Component { return ( { }; render(): React.ReactNode { + console.log(this.props.values, this.props.buttons); const values = this.props.values || []; const buttons = this.props.buttons.map((button, i) => { const selected = values.indexOf(button.value) >= 0; diff --git a/packages/perseus/src/components/number-input.tsx b/packages/perseus/src/components/number-input.tsx index 20027dbbee..ad8cfd1d79 100644 --- a/packages/perseus/src/components/number-input.tsx +++ b/packages/perseus/src/components/number-input.tsx @@ -42,7 +42,7 @@ class NumberInput extends React.Component { onChange: PropTypes.func.isRequired, onFormatChange: PropTypes.func, checkValidity: PropTypes.func, - size: PropTypes.string, + size: PropTypes.oneOf(["mini", "small", "normal"]), label: PropTypes.oneOf(["put your labels outside your inputs!"]), }; diff --git a/packages/perseus/src/components/range-input.tsx b/packages/perseus/src/components/range-input.tsx index ac0ad72368..0226f5dc45 100644 --- a/packages/perseus/src/components/range-input.tsx +++ b/packages/perseus/src/components/range-input.tsx @@ -1,33 +1,26 @@ -/* eslint-disable react/forbid-prop-types */ -import PropTypes from "prop-types"; import * as React from "react"; import NumberInput from "./number-input"; const truth = () => true; -/* A minor abstraction on top of NumberInput for ranges - * - */ -class RangeInput extends React.Component { - static propTypes = { - value: PropTypes.array.isRequired, - onChange: PropTypes.func.isRequired, - placeholder: PropTypes.array, - checkValidity: PropTypes.func, - }; +type Props = { + value: [number, number]; + placeholder: [string, string]; + onChange: (start: number, end: number) => void; + checkValidity: (vals: [number, number]) => boolean; +}; - static defaultProps: any = { - placeholder: [null, null], - }; +type DefaultProps = { + placeholder: Props["placeholder"] | [null, null]; +}; - onChange: (arg1: number, arg2: string) => void = (i, newVal) => { - const value = this.props.value; - if (i === 0) { - this.props.onChange([newVal, value[1]]); - } else { - this.props.onChange([value[0], newVal]); - } +/** + * A minor abstraction on top of NumberInput for ranges. + */ +class RangeInput extends React.Component { + static defaultProps: DefaultProps = { + placeholder: [null, null], }; render(): React.ReactNode { @@ -40,16 +33,14 @@ class RangeInput extends React.Component { {...this.props} value={value[0]} checkValidity={(val) => checkValidity([val, value[1]])} - // eslint-disable-next-line react/jsx-no-bind - onChange={this.onChange.bind(this, 0)} + onChange={(val) => this.props.onChange(val, value[1])} placeholder={this.props.placeholder[0]} /> checkValidity([value[0], val])} - // eslint-disable-next-line react/jsx-no-bind - onChange={this.onChange.bind(this, 1)} + onChange={(val: any) => this.props.onChange(value[0], val)} placeholder={this.props.placeholder[1]} />
diff --git a/packages/perseus/src/components/simple-keypad-input.tsx b/packages/perseus/src/components/simple-keypad-input.tsx index aa6f9e4511..75398fe2f0 100644 --- a/packages/perseus/src/components/simple-keypad-input.tsx +++ b/packages/perseus/src/components/simple-keypad-input.tsx @@ -1,22 +1,24 @@ +import {KeypadInput, KeypadType} from "@khanacademy/math-input"; +import * as React from "react"; + +import type {KeypadAPI} from "@khanacademy/math-input"; + +type Props = { + keypadElement: KeypadAPI; + onFocus: () => void; + value: string | number; +}; + /** * A version of the `math-input` subrepo's KeypadInput component that adheres to - * the same API as Perseus's MathOuput and NumberInput, allowing it to be + * the same API as Perseus's `MathOuput` and `NumberInput`, allowing it to be * dropped in as a replacement for those components without any modifications. * * TODO(charlie): Once the keypad API has stabilized, move this into the * `math-input` subrepo and use it everywhere as a simpler, keypad-coupled * interface to `math-input`'s MathInput component. */ - -import { - KeypadInput, - KeypadType, - keypadElementPropType, -} from "@khanacademy/math-input"; -import PropTypes from "prop-types"; -import * as React from "react"; - -export default class SimpleKeypadInput extends React.Component { +export default class SimpleKeypadInput extends React.Component { _isMounted = false; componentDidMount() { @@ -84,10 +86,3 @@ export default class SimpleKeypadInput extends React.Component { ); } } - -// @ts-expect-error - TS2339 - Property 'propTypes' does not exist on type 'typeof SimpleKeypadInput'. -SimpleKeypadInput.propTypes = { - keypadElement: keypadElementPropType, - onFocus: PropTypes.func, - value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), -}; diff --git a/packages/perseus/src/components/text-list-editor.tsx b/packages/perseus/src/components/text-list-editor.tsx index 1cefffaca8..db68f4fdaa 100644 --- a/packages/perseus/src/components/text-list-editor.tsx +++ b/packages/perseus/src/components/text-list-editor.tsx @@ -21,7 +21,7 @@ function getTextWidth(text: any) { class TextListEditor extends React.Component { static propTypes = { options: PropTypes.array, - layout: PropTypes.string, + layout: PropTypes.oneOf(["horizontal", "vertical"]), onChange: PropTypes.func.isRequired, }; diff --git a/packages/perseus/src/components/tooltip.tsx b/packages/perseus/src/components/tooltip.tsx index daba9c7fb4..53a0cc7cb0 100644 --- a/packages/perseus/src/components/tooltip.tsx +++ b/packages/perseus/src/components/tooltip.tsx @@ -1,42 +1,4 @@ /* eslint-disable react/no-unsafe */ -/** - * A generic tooltip library for React.js - * - * This should eventually end up in react-components - * - * Interface: ({a, b} means one of a or b) - * import Tooltip from "./tooltip"; - * - * - * - * - * - * - * To show/hide the tooltip, the parent component should call the - * .show() and .hide() methods of the tooltip when appropriate. - * (These are usually set up as handlers of events on the target element.) - * - * Notes: - * className should not specify a border; that is handled by borderColor - * so that the arrow and tooltip match - */ - -// __,,--``\\ -// _,,-''`` \\ , -// '----------_.------'-.___|\__ -// _.--''`` `)__ )__ @\__ -// ( .. ''---/___,,E/__,E'------` -// `-''`'' -// Here be dragons. // TODO(joel/aria) fix z-index issues https://s3.amazonaws.com/uploads.hipchat.com/6574/29028/yOApjwmgiMhEZYJ/Screen%20Shot%202014-05-30%20at%203.34.18%20PM.png // z-index: 3 on perseus-formats-tooltip seemed to work @@ -233,6 +195,47 @@ type State = { height: number | null; }; +/** + * DEPRECATED! Use Wonder Blocks tooltip instead. + * + * A generic tooltip library for React.js + * + * ``` + * import Tooltip from "./tooltip"; + * + * + * + * + * + * ``` + * + * To show/hide the tooltip, the parent component should call the + * `.show()` and `.hide()` methods of the tooltip when appropriate. + * (These are usually set up as handlers of events on the target element.) + * + * Notes: + * `className` should not specify a border; that is handled by `borderColor` + * so that the arrow and tooltip match + * + * ``` + * __,,--``\\ + * _,,-''`` \\ , + * '----------_.------'-.___|\__ + * _.--''`` `)__ )__ @\__ + * ( .. ''---/___,,E/__,E'------` + * `-''`'' + * Here be dragons. + * ``` + */ class Tooltip extends React.Component { static defaultProps: DefaultProps = { className: "", diff --git a/packages/perseus/src/icon-paths.ts b/packages/perseus/src/icon-paths.ts index 75e9ec7bd4..65b0facc70 100644 --- a/packages/perseus/src/icon-paths.ts +++ b/packages/perseus/src/icon-paths.ts @@ -1,9 +1,5 @@ /** - * Icon paths to be used with `inline-icon.jsx`. - * - * These paths are taken directly from webapp's `icon-paths.js`. Unlike the - * webapp equivalent, these can be directly required within Perseus files since - * this is all bundled together anyway. + * Icon paths to be used with `inline-icon.tsx` and `icon.tsx`. */ export const iconCheck = { From 8e7d01d36b5fbac34c47f8ebeb73d06ba2f32154 Mon Sep 17 00:00:00 2001 From: Jeremy Wiebe Date: Fri, 25 Oct 2024 09:04:09 -0700 Subject: [PATCH 02/17] Changeset --- .changeset/old-lamps-wonder.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/old-lamps-wonder.md diff --git a/.changeset/old-lamps-wonder.md b/.changeset/old-lamps-wonder.md new file mode 100644 index 0000000000..0eaff9da1e --- /dev/null +++ b/.changeset/old-lamps-wonder.md @@ -0,0 +1,6 @@ +--- +"@khanacademy/math-input": patch +"@khanacademy/perseus": patch +--- + +Improve prop types for various components From bd473357cf5d0bfca64883c8b9b68ca8d7331ffa Mon Sep 17 00:00:00 2001 From: Jeremy Wiebe Date: Fri, 25 Oct 2024 09:57:22 -0700 Subject: [PATCH 03/17] Revert type chagnes to two components - rabbit hole --- .../perseus/src/components/range-input.tsx | 43 +++++++++++-------- .../src/components/simple-keypad-input.tsx | 31 +++++++------ 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/packages/perseus/src/components/range-input.tsx b/packages/perseus/src/components/range-input.tsx index 0226f5dc45..ac0ad72368 100644 --- a/packages/perseus/src/components/range-input.tsx +++ b/packages/perseus/src/components/range-input.tsx @@ -1,28 +1,35 @@ +/* eslint-disable react/forbid-prop-types */ +import PropTypes from "prop-types"; import * as React from "react"; import NumberInput from "./number-input"; const truth = () => true; -type Props = { - value: [number, number]; - placeholder: [string, string]; - onChange: (start: number, end: number) => void; - checkValidity: (vals: [number, number]) => boolean; -}; - -type DefaultProps = { - placeholder: Props["placeholder"] | [null, null]; -}; - -/** - * A minor abstraction on top of NumberInput for ranges. +/* A minor abstraction on top of NumberInput for ranges + * */ -class RangeInput extends React.Component { - static defaultProps: DefaultProps = { +class RangeInput extends React.Component { + static propTypes = { + value: PropTypes.array.isRequired, + onChange: PropTypes.func.isRequired, + placeholder: PropTypes.array, + checkValidity: PropTypes.func, + }; + + static defaultProps: any = { placeholder: [null, null], }; + onChange: (arg1: number, arg2: string) => void = (i, newVal) => { + const value = this.props.value; + if (i === 0) { + this.props.onChange([newVal, value[1]]); + } else { + this.props.onChange([value[0], newVal]); + } + }; + render(): React.ReactNode { const value = this.props.value; const checkValidity = this.props.checkValidity || truth; @@ -33,14 +40,16 @@ class RangeInput extends React.Component { {...this.props} value={value[0]} checkValidity={(val) => checkValidity([val, value[1]])} - onChange={(val) => this.props.onChange(val, value[1])} + // eslint-disable-next-line react/jsx-no-bind + onChange={this.onChange.bind(this, 0)} placeholder={this.props.placeholder[0]} /> checkValidity([value[0], val])} - onChange={(val: any) => this.props.onChange(value[0], val)} + // eslint-disable-next-line react/jsx-no-bind + onChange={this.onChange.bind(this, 1)} placeholder={this.props.placeholder[1]} /> diff --git a/packages/perseus/src/components/simple-keypad-input.tsx b/packages/perseus/src/components/simple-keypad-input.tsx index 75398fe2f0..aa6f9e4511 100644 --- a/packages/perseus/src/components/simple-keypad-input.tsx +++ b/packages/perseus/src/components/simple-keypad-input.tsx @@ -1,24 +1,22 @@ -import {KeypadInput, KeypadType} from "@khanacademy/math-input"; -import * as React from "react"; - -import type {KeypadAPI} from "@khanacademy/math-input"; - -type Props = { - keypadElement: KeypadAPI; - onFocus: () => void; - value: string | number; -}; - /** * A version of the `math-input` subrepo's KeypadInput component that adheres to - * the same API as Perseus's `MathOuput` and `NumberInput`, allowing it to be + * the same API as Perseus's MathOuput and NumberInput, allowing it to be * dropped in as a replacement for those components without any modifications. * * TODO(charlie): Once the keypad API has stabilized, move this into the * `math-input` subrepo and use it everywhere as a simpler, keypad-coupled * interface to `math-input`'s MathInput component. */ -export default class SimpleKeypadInput extends React.Component { + +import { + KeypadInput, + KeypadType, + keypadElementPropType, +} from "@khanacademy/math-input"; +import PropTypes from "prop-types"; +import * as React from "react"; + +export default class SimpleKeypadInput extends React.Component { _isMounted = false; componentDidMount() { @@ -86,3 +84,10 @@ export default class SimpleKeypadInput extends React.Component { ); } } + +// @ts-expect-error - TS2339 - Property 'propTypes' does not exist on type 'typeof SimpleKeypadInput'. +SimpleKeypadInput.propTypes = { + keypadElement: keypadElementPropType, + onFocus: PropTypes.func, + value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), +}; From e839d6018a05eea2fb281d730db06b60e54c1546 Mon Sep 17 00:00:00 2001 From: Jeremy Wiebe Date: Fri, 25 Oct 2024 10:00:29 -0700 Subject: [PATCH 04/17] Remove console.log() --- packages/perseus/src/components/multi-button-group.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/perseus/src/components/multi-button-group.tsx b/packages/perseus/src/components/multi-button-group.tsx index 2a371142eb..f9da59911d 100644 --- a/packages/perseus/src/components/multi-button-group.tsx +++ b/packages/perseus/src/components/multi-button-group.tsx @@ -68,7 +68,6 @@ class MultiButtonGroup extends React.Component { }; render(): React.ReactNode { - console.log(this.props.values, this.props.buttons); const values = this.props.values || []; const buttons = this.props.buttons.map((button, i) => { const selected = values.indexOf(button.value) >= 0; From 85504cccb9674273318729091c451e9ea9e678e7 Mon Sep 17 00:00:00 2001 From: Jeremy Wiebe Date: Fri, 25 Oct 2024 10:01:32 -0700 Subject: [PATCH 05/17] Use standard method for accessing props --- packages/perseus/src/components/tooltip.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/perseus/src/components/tooltip.tsx b/packages/perseus/src/components/tooltip.tsx index 53a0cc7cb0..d5a13e13ea 100644 --- a/packages/perseus/src/components/tooltip.tsx +++ b/packages/perseus/src/components/tooltip.tsx @@ -58,7 +58,7 @@ const Triangle = (props: TriangleProps) => { width: 0, position: "absolute", left: props.left, - top: props["top"], + top: props.top, borderLeft: borderLeft, borderRight: borderRight, borderTop: borderTop, From 964518b6a4e7a65f6458b64e6d7c8a7c99cc12fb Mon Sep 17 00:00:00 2001 From: Jeremy Wiebe Date: Fri, 25 Oct 2024 10:12:33 -0700 Subject: [PATCH 06/17] Simplify passing onAnalyticsEvent - to help Storybook --- .../components/__tests__/math-input.test.tsx | 18 +++++++++--------- packages/perseus/src/components/math-input.tsx | 6 +++--- packages/perseus/src/types.ts | 4 ++-- .../src/widgets/expression/expression.tsx | 7 +++---- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/packages/perseus/src/components/__tests__/math-input.test.tsx b/packages/perseus/src/components/__tests__/math-input.test.tsx index f40d6c5966..56dbe88d13 100644 --- a/packages/perseus/src/components/__tests__/math-input.test.tsx +++ b/packages/perseus/src/components/__tests__/math-input.test.tsx @@ -36,7 +36,7 @@ describe("Perseus' MathInput", () => { {}} keypadButtonSets={allButtonSets} - analytics={{onAnalyticsEvent: () => Promise.resolve()}} + onAnalyticsEvent={() => Promise.resolve()} convertDotToTimes={false} value="" />, @@ -55,7 +55,7 @@ describe("Perseus' MathInput", () => { {}} keypadButtonSets={allButtonSets} - analytics={{onAnalyticsEvent: () => Promise.resolve()}} + onAnalyticsEvent={() => Promise.resolve()} convertDotToTimes={false} value="" />, @@ -74,7 +74,7 @@ describe("Perseus' MathInput", () => { {}} keypadButtonSets={allButtonSets} - analytics={{onAnalyticsEvent: () => Promise.resolve()}} + onAnalyticsEvent={() => Promise.resolve()} convertDotToTimes={false} value="" ariaLabel="Hello world" @@ -95,7 +95,7 @@ describe("Perseus' MathInput", () => { Promise.resolve()}} + onAnalyticsEvent={() => Promise.resolve()} convertDotToTimes={false} value="" />, @@ -120,7 +120,7 @@ describe("Perseus' MathInput", () => { Promise.resolve()}} + onAnalyticsEvent={() => Promise.resolve()} convertDotToTimes={false} value="" />, @@ -149,7 +149,7 @@ describe("Perseus' MathInput", () => { Promise.resolve()}} + onAnalyticsEvent={() => Promise.resolve()} convertDotToTimes={false} value="" />, @@ -178,7 +178,7 @@ describe("Perseus' MathInput", () => { {}} keypadButtonSets={allButtonSets} - analytics={{onAnalyticsEvent: () => Promise.resolve()}} + onAnalyticsEvent={() => Promise.resolve()} convertDotToTimes={false} value="" />, @@ -202,7 +202,7 @@ describe("Perseus' MathInput", () => { {}} keypadButtonSets={allButtonSets} - analytics={{onAnalyticsEvent: () => Promise.resolve()}} + onAnalyticsEvent={() => Promise.resolve()} convertDotToTimes={false} value="" />, @@ -231,7 +231,7 @@ describe("Perseus' MathInput", () => { {}} buttonsVisible="always" - analytics={{onAnalyticsEvent: () => Promise.resolve()}} + onAnalyticsEvent={() => Promise.resolve()} convertDotToTimes={false} value="" />, diff --git a/packages/perseus/src/components/math-input.tsx b/packages/perseus/src/components/math-input.tsx index 995273ae3e..c3af73f33f 100644 --- a/packages/perseus/src/components/math-input.tsx +++ b/packages/perseus/src/components/math-input.tsx @@ -29,6 +29,7 @@ import {PerseusI18nContext} from "./i18n-context"; import type {LegacyButtonSets} from "../perseus-types"; import type {PerseusDependenciesV2} from "../types"; import type {Keys, MathFieldInterface} from "@khanacademy/math-input"; +import type {AnalyticsEventHandlerFn} from "@khanacademy/perseus-core"; type ButtonsVisibleType = "always" | "never" | "focused"; @@ -68,7 +69,7 @@ type Props = { * - `never` means that the keypad is **never shown**. */ buttonsVisible?: ButtonsVisibleType; - analytics: PerseusDependenciesV2["analytics"]; + onAnalyticsEvent: AnalyticsEventHandlerFn; }; type InnerProps = Props & { @@ -352,8 +353,7 @@ class InnerMathInput extends React.Component { > {}, - } + onAnalyticsEvent={ + this.props.analytics?.onAnalyticsEvent ?? + (async () => {}) } /> From f014baa7254e43ab8aba2319bbe2df398b106f6f Mon Sep 17 00:00:00 2001 From: Jeremy Wiebe Date: Fri, 25 Oct 2024 10:17:34 -0700 Subject: [PATCH 07/17] Remove unused import --- packages/perseus/src/components/math-input.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/perseus/src/components/math-input.tsx b/packages/perseus/src/components/math-input.tsx index c3af73f33f..37aa4b0b63 100644 --- a/packages/perseus/src/components/math-input.tsx +++ b/packages/perseus/src/components/math-input.tsx @@ -27,7 +27,6 @@ import {debounce} from "../util/debounce"; import {PerseusI18nContext} from "./i18n-context"; import type {LegacyButtonSets} from "../perseus-types"; -import type {PerseusDependenciesV2} from "../types"; import type {Keys, MathFieldInterface} from "@khanacademy/math-input"; import type {AnalyticsEventHandlerFn} from "@khanacademy/perseus-core"; From 9f0c9ccda4624f96d62afe1a3aad7eeaca9d2b85 Mon Sep 17 00:00:00 2001 From: Jeremy Wiebe Date: Mon, 28 Oct 2024 13:24:53 -0700 Subject: [PATCH 08/17] Revert moving graph.tsx to use regular React refs - broke tests --- packages/perseus/src/components/graph.tsx | 27 +++++++++++++---------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/packages/perseus/src/components/graph.tsx b/packages/perseus/src/components/graph.tsx index 08674fa5ff..e8e8af9210 100644 --- a/packages/perseus/src/components/graph.tsx +++ b/packages/perseus/src/components/graph.tsx @@ -3,6 +3,7 @@ import {vector as kvector} from "@khanacademy/kmath"; import $ from "jquery"; import * as React from "react"; +import ReactDOM from "react-dom"; import _ from "underscore"; import AssetContext from "../asset-context"; @@ -85,9 +86,10 @@ class Graph extends React.Component { protractor: any; ruler: any; _graphie: any; - _hasSetupGraphieThisUpdate = false; - _shouldSetupGraphie = true; - graphieDiv = React.createRef(); + // @ts-expect-error - TS2564 - Property '_hasSetupGraphieThisUpdate' has no initializer and is not definitely assigned in the constructor. + _hasSetupGraphieThisUpdate: boolean; + // @ts-expect-error - TS2564 - Property '_shouldSetupGraphie' has no initializer and is not definitely assigned in the constructor. + _shouldSetupGraphie: boolean; static defaultProps: DefaultProps = { labels: ["x", "y"], @@ -184,11 +186,11 @@ class Graph extends React.Component { if (this._hasSetupGraphieThisUpdate) { return; } - if (this.graphieDiv.current == null) { - return; - } - $(this.graphieDiv.current).empty(); + // eslint-disable-next-line react/no-string-refs + const graphieDiv = ReactDOM.findDOMNode(this.refs.graphieDiv); + // @ts-expect-error - TS2769 - No overload matches this call. | TS2339 - Property 'empty' does not exist on type 'JQueryStatic'. + $(graphieDiv).empty(); // Content creators may need to explicitly add the dollar signs so the // strings are picked up by our translation tools. However, these math @@ -200,9 +202,8 @@ class Graph extends React.Component { Util.unescapeMathMode(label), ); const range = this.props.range; - const graphie = (this._graphie = GraphUtils.createGraphie( - this.graphieDiv.current, - )); + // @ts-expect-error - TS2345: Argument of type 'Element | Text | null' is not assignable to parameter of type 'HTMLElement'. + const graphie = (this._graphie = GraphUtils.createGraphie(graphieDiv)); const gridConfig: [GridDimensions, GridDimensions] = this._getGridConfig(); @@ -270,7 +271,8 @@ class Graph extends React.Component { }); $instructionsWrapper.append($instructions); - $(this.graphieDiv.current).append($instructionsWrapper); + // @ts-expect-error - TS2769 - No overload matches this call. | TS2339 - Property 'append' does not exist on type 'JQueryStatic'. + $(graphieDiv).append($instructionsWrapper); } else { $instructionsWrapper = undefined; } @@ -429,7 +431,8 @@ class Graph extends React.Component { onClick={this.onClick} > {image} -
+ {/* eslint-disable-next-line react/no-string-refs */} +
); } From 6315ec896e6dbb3b5f8138a5db5c2bc2a7c8ae87 Mon Sep 17 00:00:00 2001 From: Jeremy Wiebe Date: Thu, 31 Oct 2024 12:05:40 -0700 Subject: [PATCH 09/17] Update packages/perseus/src/components/__stories__/input-with-examples.stories.tsx Co-authored-by: Nisha Yerunkar --- .../src/components/__stories__/input-with-examples.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/perseus/src/components/__stories__/input-with-examples.stories.tsx b/packages/perseus/src/components/__stories__/input-with-examples.stories.tsx index 95583794fa..1c6a226baf 100644 --- a/packages/perseus/src/components/__stories__/input-with-examples.stories.tsx +++ b/packages/perseus/src/components/__stories__/input-with-examples.stories.tsx @@ -40,7 +40,7 @@ export const AriaLabelTextWithListOfExamples: Story = { }, }; -export const DisabledInput = { +export const DisabledInput: Story = { args: { disabled: true, examples: testExamples, From 3bb6fdcf841672091f3e56b39ad8c5e9f82450f1 Mon Sep 17 00:00:00 2001 From: Jeremy Wiebe Date: Thu, 31 Oct 2024 12:03:59 -0700 Subject: [PATCH 10/17] Fix usage of action() helper from `@storybook/addon-actions` --- .../__stories__/input-with-examples.stories.tsx | 4 ++-- .../src/components/__stories__/math-input.stories.tsx | 4 ++-- .../components/__stories__/stub-tag-editor.stories.tsx | 4 ++-- .../src/components/__stories__/text-input.stories.tsx | 8 ++++---- .../components/__stories__/text-list-editor.stories.tsx | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/perseus/src/components/__stories__/input-with-examples.stories.tsx b/packages/perseus/src/components/__stories__/input-with-examples.stories.tsx index 1c6a226baf..ecf4142066 100644 --- a/packages/perseus/src/components/__stories__/input-with-examples.stories.tsx +++ b/packages/perseus/src/components/__stories__/input-with-examples.stories.tsx @@ -1,4 +1,4 @@ -import {actions} from "@storybook/addon-actions"; +import {action} from "@storybook/addon-actions"; import InputWithExamples from "../input-with-examples"; @@ -10,7 +10,7 @@ const meta: Meta = { args: { examples: [], id: "", - onChange: actions("onChange"), + onChange: action("onChange"), value: "", }, argTypes: { diff --git a/packages/perseus/src/components/__stories__/math-input.stories.tsx b/packages/perseus/src/components/__stories__/math-input.stories.tsx index a009812a87..165c8abea6 100644 --- a/packages/perseus/src/components/__stories__/math-input.stories.tsx +++ b/packages/perseus/src/components/__stories__/math-input.stories.tsx @@ -1,4 +1,4 @@ -import {actions} from "@storybook/addon-actions"; +import {action} from "@storybook/addon-actions"; import MathInput from "../math-input"; @@ -18,7 +18,7 @@ const meta: Meta = { }, convertDotToTimes: false, value: "", - onChange: actions("onChange"), + onChange: action("onChange"), analytics: {onAnalyticsEvent: () => Promise.resolve()}, labelText: "Math input", }, diff --git a/packages/perseus/src/components/__stories__/stub-tag-editor.stories.tsx b/packages/perseus/src/components/__stories__/stub-tag-editor.stories.tsx index 2480fab74a..075aeca9f7 100644 --- a/packages/perseus/src/components/__stories__/stub-tag-editor.stories.tsx +++ b/packages/perseus/src/components/__stories__/stub-tag-editor.stories.tsx @@ -1,4 +1,4 @@ -import {actions} from "@storybook/addon-actions"; +import {action} from "@storybook/addon-actions"; import StubTagEditor from "../stub-tag-editor"; @@ -9,7 +9,7 @@ const meta: Meta = { component: StubTagEditor, args: { value: [], - onChange: actions("onChange"), + onChange: action("onChange"), }, argTypes: { onChange: { diff --git a/packages/perseus/src/components/__stories__/text-input.stories.tsx b/packages/perseus/src/components/__stories__/text-input.stories.tsx index ec687be8e6..3034cab15e 100644 --- a/packages/perseus/src/components/__stories__/text-input.stories.tsx +++ b/packages/perseus/src/components/__stories__/text-input.stories.tsx @@ -1,4 +1,4 @@ -import {actions} from "@storybook/addon-actions"; +import {action} from "@storybook/addon-actions"; import TextInput from "../text-input"; @@ -8,9 +8,9 @@ const meta: Meta = { title: "Perseus/Components/Text Input", component: TextInput, args: { - onChange: actions("onChange"), - onBlur: actions("onBlur"), - onFocus: actions("onFocus"), + onChange: action("onChange"), + onBlur: action("onBlur"), + onFocus: action("onFocus"), }, argTypes: { onChange: {table: {disable: true}}, diff --git a/packages/perseus/src/components/__stories__/text-list-editor.stories.tsx b/packages/perseus/src/components/__stories__/text-list-editor.stories.tsx index 568ffabb11..b179fb65b7 100644 --- a/packages/perseus/src/components/__stories__/text-list-editor.stories.tsx +++ b/packages/perseus/src/components/__stories__/text-list-editor.stories.tsx @@ -1,4 +1,4 @@ -import {actions} from "@storybook/addon-actions"; +import {action} from "@storybook/addon-actions"; import * as React from "react"; import TextListEditor from "../text-list-editor"; @@ -10,7 +10,7 @@ const meta: Meta = { component: TextListEditor, args: { options: ["Test option 1", "Test option 2", "Test option 3"], - onChange: actions("onChange"), + onChange: action("onChange"), }, argTypes: { onChange: {table: {disable: true}}, From c7a34cd22bd17d8be39ad47642bf158d57e62dea Mon Sep 17 00:00:00 2001 From: Jeremy Wiebe Date: Thu, 31 Oct 2024 12:04:18 -0700 Subject: [PATCH 11/17] Move import type into type imports section --- packages/perseus/src/widgets/interactive-graphs/mafs-graph.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/perseus/src/widgets/interactive-graphs/mafs-graph.tsx b/packages/perseus/src/widgets/interactive-graphs/mafs-graph.tsx index 767cd715f3..7041b27cb4 100644 --- a/packages/perseus/src/widgets/interactive-graphs/mafs-graph.tsx +++ b/packages/perseus/src/widgets/interactive-graphs/mafs-graph.tsx @@ -41,11 +41,11 @@ import {SvgDefs} from "./graphs/components/text-label"; import {PointGraph} from "./graphs/point"; import {MIN, X, Y} from "./math"; import {Protractor} from "./protractor"; -import {type InteractiveGraphAction} from "./reducer/interactive-graph-action"; import {actions} from "./reducer/interactive-graph-action"; import {GraphConfigContext} from "./reducer/use-graph-config"; import {isUnlimitedGraphState, REMOVE_BUTTON_ID} from "./utils"; +import type {InteractiveGraphAction} from "./reducer/interactive-graph-action"; import type { InteractiveGraphState, InteractiveGraphProps, From e974f36278944259304a5ac3585cdd17afe52c8a Mon Sep 17 00:00:00 2001 From: Jeremy Wiebe Date: Thu, 31 Oct 2024 12:29:07 -0700 Subject: [PATCH 12/17] Add better doc comments to MultiButtonGroup and its stories --- .../multi-button-group.stories.tsx | 3 ++ .../src/components/multi-button-group.tsx | 36 +++++++++++++------ 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/packages/perseus/src/components/__stories__/multi-button-group.stories.tsx b/packages/perseus/src/components/__stories__/multi-button-group.stories.tsx index 36305c37f5..85e3ef479b 100644 --- a/packages/perseus/src/components/__stories__/multi-button-group.stories.tsx +++ b/packages/perseus/src/components/__stories__/multi-button-group.stories.tsx @@ -37,6 +37,9 @@ export const ButtonsWithNoTitles: Story = { }, }; +/** + * Hover over the buttons to see their titles. + */ export const ButtonsWithTitles: Story = { args: { buttons: [ diff --git a/packages/perseus/src/components/multi-button-group.tsx b/packages/perseus/src/components/multi-button-group.tsx index f9da59911d..533334addf 100644 --- a/packages/perseus/src/components/multi-button-group.tsx +++ b/packages/perseus/src/components/multi-button-group.tsx @@ -3,23 +3,39 @@ import * as React from "react"; import * as ReactDOM from "react-dom"; type Props = { - // the initial values of the buttons selected, defaults to null (no - // selection). + /** + * The initial values of the buttons selected, defaults to null (no + * selection). + */ values: ReadonlyArray | null | undefined; + /** + * The set of buttons to display in this MultiButtonGroup. + */ buttons: ReadonlyArray<{ - // the value returned when the button is selected + /** + * the value returned when the button is selected + */ value: any; - // the content shown within the button, typically a string that gets - // rendered as the button's display text + /** + * The content shown within the button, typically a string that gets + * rendered as the button's display text. + */ content: React.ReactNode; - // the title-text shown on hover + /** + * The title-text shown on hover + */ title?: string; }>; - // a function that is provided with the updated set of selected value - // (which it then is responsible for updating) + /** + * A function that is provided with the updated set of selected value + * (which it then is responsible for updating) + */ onChange: (values?: any) => unknown; - // if false, at least one button must be selected at all times. - // defaults to true + /** + * If false, at least one button must be selected at all times. + * + * Defaults to `true` + */ allowEmpty?: boolean; }; From 3460825602697e310eb25198fec2682151a05f84 Mon Sep 17 00:00:00 2001 From: Jeremy Wiebe Date: Thu, 31 Oct 2024 13:07:26 -0700 Subject: [PATCH 13/17] Feedback --- .../__stories__/image-loader.stories.tsx | 8 ++++ .../input-with-examples.stories.tsx | 2 +- .../__stories__/svg-image.stories.tsx | 12 +++++ packages/perseus/src/components/svg-image.tsx | 48 ++++++++++++------- 4 files changed, 51 insertions(+), 19 deletions(-) diff --git a/packages/perseus/src/components/__stories__/image-loader.stories.tsx b/packages/perseus/src/components/__stories__/image-loader.stories.tsx index 094cebdffb..433764a5fd 100644 --- a/packages/perseus/src/components/__stories__/image-loader.stories.tsx +++ b/packages/perseus/src/components/__stories__/image-loader.stories.tsx @@ -17,6 +17,14 @@ const meta: Meta = { }, onUpdate: () => {}, }, + parameters: { + chromatic: { + // This component only deals with loading images and providing a + // fallback if it fails. This is not very useful to snapshot so + // we're disabling it. + disableSnapshot: true, + }, + }, }; export default meta; diff --git a/packages/perseus/src/components/__stories__/input-with-examples.stories.tsx b/packages/perseus/src/components/__stories__/input-with-examples.stories.tsx index ecf4142066..884b93c800 100644 --- a/packages/perseus/src/components/__stories__/input-with-examples.stories.tsx +++ b/packages/perseus/src/components/__stories__/input-with-examples.stories.tsx @@ -15,7 +15,7 @@ const meta: Meta = { }, argTypes: { onChange: { - table: {disable: true}, + control: {type: null}, }, }, }; diff --git a/packages/perseus/src/components/__stories__/svg-image.stories.tsx b/packages/perseus/src/components/__stories__/svg-image.stories.tsx index a9041ecb74..43c4158c8f 100644 --- a/packages/perseus/src/components/__stories__/svg-image.stories.tsx +++ b/packages/perseus/src/components/__stories__/svg-image.stories.tsx @@ -6,6 +6,14 @@ const meta: Meta = { title: "Perseus/Components/SVG Image", component: SvgImage, args: {alt: "ALT"}, + parameters: { + chromatic: { + // This component loads images from remote sources. We need to tell + // Chromatic to wait a bit before taking a snapshot otherwise we + // get only empty snapshots. + delay: 100, + }, + }, }; export default meta; @@ -16,6 +24,10 @@ const imgUrl = "https://www.khanacademy.org/images/hand-tree.new.png"; const graphieUrl = "web+graphie://ka-perseus-graphie.s3.amazonaws.com/1e06f6d4071f30cee2cc3ccb7435b3a66a62fe3f"; +/** + * Demostrates an `SvgImage` that doesn't have a `src` defined, and as such + * never loads (infinite spinner) + */ export const Default: Story = {}; export const SvgImageThatDoesntLoad: Story = { diff --git a/packages/perseus/src/components/svg-image.tsx b/packages/perseus/src/components/svg-image.tsx index 9324c6150b..affde9dadc 100644 --- a/packages/perseus/src/components/svg-image.tsx +++ b/packages/perseus/src/components/svg-image.tsx @@ -149,34 +149,46 @@ type Props = { labels: ReadonlyArray; }; height?: number; - // When the DOM updates to replace the preloader with the image, or - // vice-versa, we trigger this callback. + /** + * When the DOM updates to replace the preloader with the image, or + * vice-versa, we trigger this callback. + */ onUpdate: () => void; - // If alt is provided, DO NOT set aria-hidden=true unless this override flag - // is set. + /** + * If alt is provided, DO NOT set aria-hidden=true unless this override flag + * is set. + */ overrideAriaHidden?: boolean; preloader?: (dimensions: Dimensions) => React.ReactNode; - // By default, this component attempts to be responsive whenever - // possible (specifically, when width and height are passed in). - // You can expliclty force unresponsive behavior by *either* - // not passing in width/height *or* setting this prop to false. - // The difference is that forcing via this prop will result in - // explicit width and height styles being set on the rendered - // component. + /** + * By default, this component attempts to be responsive whenever + * possible (specifically, when width and height are passed in). + * + * You can expliclty force unresponsive behavior by *either* + * not passing in width/height *or* setting this prop to false. + * + * The difference is that forcing via this prop will result in + * explicit width and height styles being set on the rendered + * component. + */ responsive: boolean; scale: number; src: string; title?: string; trackInteraction?: () => void; width?: number; - // Whether clicking this image will allow it to be fully zoomed in to - // its original size on click, and allow the user to scroll in that - // state. This also does some hacky viewport meta tag changing to - // ensure this works on mobile devices, so I (david@) don't recommend - // enabling this on desktop yet. + /** + * Whether clicking this image will allow it to be fully zoomed in to + * its original size on click, and allow the user to scroll in that + * state. This also does some hacky viewport meta tag changing to + * ensure this works on mobile devices, so I (david@) don't recommend + * enabling this on desktop yet. + */ zoomToFullSizeOnMobile?: boolean; - // If provided, use AssetContext.Consumer, see renderer.jsx. - // If not, it defaults to a no-op. + /** + * If provided, use AssetContext.Consumer, see renderer.jsx. + * If not, it defaults to a no-op. + */ setAssetStatus: (assetKey: string, loaded: boolean) => void; }; From 1829849954d4f1708733268275f05258f0b24153 Mon Sep 17 00:00:00 2001 From: Jeremy Wiebe Date: Thu, 31 Oct 2024 13:26:22 -0700 Subject: [PATCH 14/17] Change comments so they can shot up in Storybook --- packages/perseus/src/components/lint.tsx | 22 +++++++++------- .../perseus/src/components/number-input.tsx | 26 ++++++++++++------- .../perseus/src/components/range-input.tsx | 4 +-- .../src/components/stub-tag-editor.tsx | 14 +++++----- 4 files changed, 38 insertions(+), 28 deletions(-) diff --git a/packages/perseus/src/components/lint.tsx b/packages/perseus/src/components/lint.tsx index d53dadf9d9..5620ef590f 100644 --- a/packages/perseus/src/components/lint.tsx +++ b/packages/perseus/src/components/lint.tsx @@ -20,21 +20,25 @@ enum Severity { } type Props = { - // The children are the linty content we're highlighting + /** The children are the linty content we're highlighting */ children: React.ReactNode; - // Inline lint is highlighted differently than block lint. + /** Inline lint is highlighted differently than block lint. */ inline?: boolean; - // This is the text that appears in the tooltip + /** This is the text that appears in the tooltip */ message: string; - // This is used as the fragment id (hash) in the URL of the link + /** This is used as the fragment id (hash) in the URL of the link */ ruleName: string; - // Lint warnings inside tables are handled specially + /** Lint warnings inside tables are handled specially */ insideTable: boolean; - // Should lint highlighting be rendered as a block to the left of - // the lint instead of on the right gutter? + /** + * Should lint highlighting be rendered as a block to the left of + * the lint instead of on the right gutter? + */ blockHighlight?: boolean; - // How important this lint message is for the editor. Severity goes - // from 1 (indicating an error) to 4 (offline reporting only) + /** + * How important this lint message is for the editor. Severity goes + * from 1 (indicating an error) to 4 (offline reporting only) + */ severity?: Severity; }; diff --git a/packages/perseus/src/components/number-input.tsx b/packages/perseus/src/components/number-input.tsx index ad8cfd1d79..2d7f20efd8 100644 --- a/packages/perseus/src/components/number-input.tsx +++ b/packages/perseus/src/components/number-input.tsx @@ -18,18 +18,24 @@ const {firstNumericalParse, captureScratchpadTouchStart} = Util; const toNumericString = KhanMath.toNumericString; const getNumericFormat = KhanMath.getNumericFormat; -/* An input box that accepts only numeric strings +/** + * An input box that accepts only numeric strings * - * Calls onChange(value, format) for valid numbers. - * Reverts to the current value onBlur or on [ENTER], + * Calls `onChange(value, format)` for valid numbers. + * + * Reverts to the current value `onBlur` or on [ENTER], * but maintains the format (i.e. 3/2, 1 1/2, 150%) - * Accepts empty input and sends it to onChange as null - * if no numeric placeholder is set. - * If given a checkValidity function, will turn - * the background/outline red when invalid - * If useArrowKeys is set to true, up/down arrows will - * increment/decrement integers - * Optionally takes a size ("mini", "small", "normal") + * + * Accepts empty input and sends it to `onChange` as `null` if no numeric + * placeholder is set. + * + * If given a `checkValidity` function, will turn the background/outline red + * when invalid. + * + * If `useArrowKeys` is set to `true`, up/down arrows will increment/decrement + * integers. + * + * Optionally takes a `size` (`"mini"`, `"small"`,` `"normal"`) */ class NumberInput extends React.Component { static contextType = PerseusI18nContext; diff --git a/packages/perseus/src/components/range-input.tsx b/packages/perseus/src/components/range-input.tsx index ac0ad72368..b60dcad131 100644 --- a/packages/perseus/src/components/range-input.tsx +++ b/packages/perseus/src/components/range-input.tsx @@ -6,8 +6,8 @@ import NumberInput from "./number-input"; const truth = () => true; -/* A minor abstraction on top of NumberInput for ranges - * +/** + * A minor abstraction on top of `NumberInput` for ranges */ class RangeInput extends React.Component { static propTypes = { diff --git a/packages/perseus/src/components/stub-tag-editor.tsx b/packages/perseus/src/components/stub-tag-editor.tsx index da74557a5f..ca7e2a07df 100644 --- a/packages/perseus/src/components/stub-tag-editor.tsx +++ b/packages/perseus/src/components/stub-tag-editor.tsx @@ -1,3 +1,10 @@ +import PropTypes from "prop-types"; +import * as React from "react"; + +import TextListEditor from "./text-list-editor"; + +const EMPTY_ARRAY = []; + /** * Stub Tag Editor. * @@ -11,13 +18,6 @@ * It also gives a nicer interface for the group metadata editor * in local demo mode. */ -import PropTypes from "prop-types"; -import * as React from "react"; - -import TextListEditor from "./text-list-editor"; - -const EMPTY_ARRAY = []; - class StubTagEditor extends React.Component { static propTypes = { value: PropTypes.arrayOf(PropTypes.string), From e6c11d53be65c30645bae776ffc921fdd6fedbae Mon Sep 17 00:00:00 2001 From: Jeremy Wiebe Date: Thu, 31 Oct 2024 13:26:34 -0700 Subject: [PATCH 15/17] Disable controls for callback props instead of hiding them --- .../src/components/__stories__/graphie.stories.tsx | 4 ++++ .../perseus/src/components/__stories__/lint.stories.tsx | 2 +- .../src/components/__stories__/math-input.stories.tsx | 4 ++-- .../src/components/__stories__/number-input.stories.tsx | 4 ++-- .../src/components/__stories__/range-input.stories.tsx | 2 +- .../components/__stories__/stub-tag-editor.stories.tsx | 2 +- .../src/components/__stories__/svg-image.stories.tsx | 6 ++---- .../src/components/__stories__/text-input.stories.tsx | 6 +++--- .../components/__stories__/text-list-editor.stories.tsx | 2 +- .../src/components/__stories__/zoomable.stories.tsx | 9 ++++++++- 10 files changed, 25 insertions(+), 16 deletions(-) diff --git a/packages/perseus/src/components/__stories__/graphie.stories.tsx b/packages/perseus/src/components/__stories__/graphie.stories.tsx index 264f94f6c9..e0d779d4ca 100644 --- a/packages/perseus/src/components/__stories__/graphie.stories.tsx +++ b/packages/perseus/src/components/__stories__/graphie.stories.tsx @@ -23,6 +23,10 @@ export default meta; export const SquareBoxSizeAndOtherwiseEmpty: Story = {}; +/** + * A demonstration of a Graphie rendered using the Perseus `Renderer` complete + * with overlaid labels and an image caption below. + */ export const PieChartGraphieLabels = () => { return ; }; diff --git a/packages/perseus/src/components/__stories__/lint.stories.tsx b/packages/perseus/src/components/__stories__/lint.stories.tsx index 8813554429..7f4c96463a 100644 --- a/packages/perseus/src/components/__stories__/lint.stories.tsx +++ b/packages/perseus/src/components/__stories__/lint.stories.tsx @@ -31,7 +31,7 @@ const meta: Meta = { ruleName: "Test rule", }, argTypes: { - children: {table: {disable: true}}, + children: {control: {type: null}}, severity: { type: "number", control: { diff --git a/packages/perseus/src/components/__stories__/math-input.stories.tsx b/packages/perseus/src/components/__stories__/math-input.stories.tsx index 165c8abea6..3f4264f9a9 100644 --- a/packages/perseus/src/components/__stories__/math-input.stories.tsx +++ b/packages/perseus/src/components/__stories__/math-input.stories.tsx @@ -24,10 +24,10 @@ const meta: Meta = { }, argTypes: { onChange: { - table: {disable: true}, + control: {type: null}, }, analytics: { - table: {disable: true}, + control: {type: null}, }, }, parameters: { diff --git a/packages/perseus/src/components/__stories__/number-input.stories.tsx b/packages/perseus/src/components/__stories__/number-input.stories.tsx index 6c7757cdef..ee10c62785 100644 --- a/packages/perseus/src/components/__stories__/number-input.stories.tsx +++ b/packages/perseus/src/components/__stories__/number-input.stories.tsx @@ -12,8 +12,8 @@ const meta: Meta = { onFormatChange: action("onFormatChange"), }, argTypes: { - onChange: {table: {disable: true}}, - onFormatChange: {table: {disable: true}}, + onChange: {control: {type: null}}, + onFormatChange: {control: {type: null}}, }, }; export default meta; diff --git a/packages/perseus/src/components/__stories__/range-input.stories.tsx b/packages/perseus/src/components/__stories__/range-input.stories.tsx index de357c34a9..fd4a11bb0f 100644 --- a/packages/perseus/src/components/__stories__/range-input.stories.tsx +++ b/packages/perseus/src/components/__stories__/range-input.stories.tsx @@ -12,7 +12,7 @@ const meta: Meta = { onChange: action("onChange"), }, argTypes: { - onChange: {table: {disable: true}}, + onChange: {control: {type: null}}, }, }; export default meta; diff --git a/packages/perseus/src/components/__stories__/stub-tag-editor.stories.tsx b/packages/perseus/src/components/__stories__/stub-tag-editor.stories.tsx index 075aeca9f7..73ebfb39d1 100644 --- a/packages/perseus/src/components/__stories__/stub-tag-editor.stories.tsx +++ b/packages/perseus/src/components/__stories__/stub-tag-editor.stories.tsx @@ -13,7 +13,7 @@ const meta: Meta = { }, argTypes: { onChange: { - table: {disable: true}, + control: {type: null}, }, }, }; diff --git a/packages/perseus/src/components/__stories__/svg-image.stories.tsx b/packages/perseus/src/components/__stories__/svg-image.stories.tsx index 43c4158c8f..cea36c3d53 100644 --- a/packages/perseus/src/components/__stories__/svg-image.stories.tsx +++ b/packages/perseus/src/components/__stories__/svg-image.stories.tsx @@ -8,10 +8,8 @@ const meta: Meta = { args: {alt: "ALT"}, parameters: { chromatic: { - // This component loads images from remote sources. We need to tell - // Chromatic to wait a bit before taking a snapshot otherwise we - // get only empty snapshots. - delay: 100, + // The Svg + disable: 100, }, }, }; diff --git a/packages/perseus/src/components/__stories__/text-input.stories.tsx b/packages/perseus/src/components/__stories__/text-input.stories.tsx index 3034cab15e..94ee0a84c5 100644 --- a/packages/perseus/src/components/__stories__/text-input.stories.tsx +++ b/packages/perseus/src/components/__stories__/text-input.stories.tsx @@ -13,9 +13,9 @@ const meta: Meta = { onFocus: action("onFocus"), }, argTypes: { - onChange: {table: {disable: true}}, - onBlur: {table: {disable: true}}, - onFocus: {table: {disable: true}}, + onChange: {control: {type: null}}, + onBlur: {control: {type: null}}, + onFocus: {control: {type: null}}, }, }; export default meta; diff --git a/packages/perseus/src/components/__stories__/text-list-editor.stories.tsx b/packages/perseus/src/components/__stories__/text-list-editor.stories.tsx index b179fb65b7..cbf26234c9 100644 --- a/packages/perseus/src/components/__stories__/text-list-editor.stories.tsx +++ b/packages/perseus/src/components/__stories__/text-list-editor.stories.tsx @@ -13,7 +13,7 @@ const meta: Meta = { onChange: action("onChange"), }, argTypes: { - onChange: {table: {disable: true}}, + onChange: {control: {type: null}}, }, decorators: [ (Story) => ( diff --git a/packages/perseus/src/components/__stories__/zoomable.stories.tsx b/packages/perseus/src/components/__stories__/zoomable.stories.tsx index 80fd1984f5..8e7093cc2c 100644 --- a/packages/perseus/src/components/__stories__/zoomable.stories.tsx +++ b/packages/perseus/src/components/__stories__/zoomable.stories.tsx @@ -26,7 +26,14 @@ const meta: Meta = { computeChildBounds, }, argTypes: { - children: {table: {disable: true}}, + children: {control: {type: null}}, + }, + parameters: { + chromatic: { + // Disable the snapshot for this story because it's testing + // behavior, not visuals. + disableSnapshot: true, + }, }, }; export default meta; From 3e9cca93b1a4780450d6ab07c18259a154f58c9b Mon Sep 17 00:00:00 2001 From: Jeremy Wiebe Date: Thu, 31 Oct 2024 14:02:23 -0700 Subject: [PATCH 16/17] Disable Chromatic snapshots (using a consistent setting name 'disableSnapshot') --- .../src/__stories__/interactive-graph-editor.stories.tsx | 4 ++-- .../__stories__/scrollless-number-text-field.stories.tsx | 4 ++-- .../src/components/__stories__/svg-image.stories.tsx | 6 ------ 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/perseus-editor/src/__stories__/interactive-graph-editor.stories.tsx b/packages/perseus-editor/src/__stories__/interactive-graph-editor.stories.tsx index 832b73d6c7..51137cfd45 100644 --- a/packages/perseus-editor/src/__stories__/interactive-graph-editor.stories.tsx +++ b/packages/perseus-editor/src/__stories__/interactive-graph-editor.stories.tsx @@ -172,7 +172,7 @@ MafsWithLockedFiguresCurrent.parameters = { chromatic: { // Disabling because this isn't visually testing anything on the // initial load of the editor page. - disable: true, + disableSnapshot: true, }, }; @@ -413,7 +413,7 @@ WithSaveWarnings.parameters = { // Disabling because this isn't testing anything visually on the // editor page. It's testing the error message, which don't // even show up on the initial load. - disable: true, + disableSnapshot: true, }, }; diff --git a/packages/perseus-editor/src/components/__stories__/scrollless-number-text-field.stories.tsx b/packages/perseus-editor/src/components/__stories__/scrollless-number-text-field.stories.tsx index 7a00a45a63..6fabb8ce98 100644 --- a/packages/perseus-editor/src/components/__stories__/scrollless-number-text-field.stories.tsx +++ b/packages/perseus-editor/src/components/__stories__/scrollless-number-text-field.stories.tsx @@ -45,7 +45,7 @@ Controlled.parameters = { chromatic: { // Disable the snapshot for this story because it's testing // behavior, not visuals. - disable: true, + disableSnapshot: true, }, }; @@ -77,6 +77,6 @@ LongPageScroll.parameters = { chromatic: { // Disable the snapshot for this story because it's testing // behavior, not visuals. - disable: true, + disableSnapshot: true, }, }; diff --git a/packages/perseus/src/components/__stories__/svg-image.stories.tsx b/packages/perseus/src/components/__stories__/svg-image.stories.tsx index cea36c3d53..04b352bc0e 100644 --- a/packages/perseus/src/components/__stories__/svg-image.stories.tsx +++ b/packages/perseus/src/components/__stories__/svg-image.stories.tsx @@ -6,12 +6,6 @@ const meta: Meta = { title: "Perseus/Components/SVG Image", component: SvgImage, args: {alt: "ALT"}, - parameters: { - chromatic: { - // The Svg - disable: 100, - }, - }, }; export default meta; From d6c8b22ab38add7b0d9bd3a5329a60483ae7409b Mon Sep 17 00:00:00 2001 From: Jeremy Wiebe Date: Thu, 31 Oct 2024 14:18:26 -0700 Subject: [PATCH 17/17] One more fix --- .../__stories__/svg-image.stories.tsx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/perseus/src/components/__stories__/svg-image.stories.tsx b/packages/perseus/src/components/__stories__/svg-image.stories.tsx index 04b352bc0e..907abbc789 100644 --- a/packages/perseus/src/components/__stories__/svg-image.stories.tsx +++ b/packages/perseus/src/components/__stories__/svg-image.stories.tsx @@ -16,11 +16,13 @@ const imgUrl = "https://www.khanacademy.org/images/hand-tree.new.png"; const graphieUrl = "web+graphie://ka-perseus-graphie.s3.amazonaws.com/1e06f6d4071f30cee2cc3ccb7435b3a66a62fe3f"; -/** - * Demostrates an `SvgImage` that doesn't have a `src` defined, and as such - * never loads (infinite spinner) - */ -export const Default: Story = {}; +export const Default: Story = { + parameters: { + /** This story doesn't provide a src url and so just shows a spinner. + * Perhaps not useful, but for now we'll just disable snapshots. */ + chromatic: {disableSnapshot: true}, + }, +}; export const SvgImageThatDoesntLoad: Story = { args: { @@ -28,6 +30,11 @@ export const SvgImageThatDoesntLoad: Story = { width: 500, src: "http://httpstat.us/200?sleep=1000000", }, + parameters: { + /** This story never loads and so just shows a spinner. Perhaps not + * useful, but for now we'll just disable snapshots. */ + chromatic: {disableSnapshot: true}, + }, }; export const SvgImageBasic: Story = {