concord-consortium/lara

View on GitHub
lara-typescript/src/page-item-authoring/managed-interactives/index.tsx

Summary

Maintainability
D
2 days
Test Coverage
import * as React from "react";
import { useCallback, useState, useRef } from "react";
import { Tabs, TabList, Tab, TabPanel} from "react-tabs";
import { ILibraryInteractive } from "../common/hooks/use-library-interactives";
import { InteractiveAuthoring } from "../common/components/interactive-authoring";
import { CustomizeManagedInteractive } from "./customize";
import { Checkbox } from "../common/components/checkbox";
import { useCurrentUser } from "../common/hooks/use-current-user";
import { AuthoredState } from "../common/components/authored-state";
import { AuthoringApiUrls } from "../common/types";
import { ILinkedInteractive } from "../../interactive-api-client";

import "react-tabs/style/react-tabs.css";
import { useAuthoringPreview } from "../common/hooks/use-authoring-preview";

interface Props {
  managedInteractive: IManagedInteractive;
  libraryInteractive?: ILibraryInteractive;
  defaultClickToPlayPrompt: string;
  authoringApiUrls: AuthoringApiUrls;
  onUpdate?: (updates: Partial<IManagedInteractive>) => void;
  handleUpdateItemPreview?: (updates: Record<string, any>) => void;
}

export interface IManagedInteractive {
  id: number;
  library_interactive_id: number;
  name: string;
  url_fragment: string;
  authored_state: string;
  is_hidden: boolean;
  is_half_width: boolean;
  aspect_ratio: number;
  enable_learner_state: boolean;
  linked_interactive_id: number;
  linked_interactive_type: string;
  show_in_featured_question_report: boolean;
  inherit_aspect_ratio_method: boolean;
  custom_aspect_ratio_method: string;
  inherit_native_width: boolean;
  custom_native_width: number;
  inherit_native_height: boolean;
  custom_native_height: number;
  inherit_click_to_play: boolean;
  custom_click_to_play: boolean;
  inherit_full_window: boolean;
  custom_full_window: boolean;
  inherit_click_to_play_prompt: boolean;
  custom_click_to_play_prompt: string;
  inherit_image_url: boolean;
  custom_image_url: string;
  interactive_item_id: string;
  linked_interactive_item_id: string;
  linked_interactives: ILinkedInteractive[];
}

export const ManagedInteractiveAuthoring: React.FC<Props> = (props) => {
  const {
    libraryInteractive,
    managedInteractive,
    defaultClickToPlayPrompt,
    authoringApiUrls,
    handleUpdateItemPreview
  } = props;
  const { name, is_half_width } = managedInteractive;
  const libraryInteractiveIdRef = useRef<HTMLInputElement|null>(null);
  const interactiveAuthoredStateRef = useRef<HTMLTextAreaElement|null>(null);
  const linkedInteractivesRef = useRef<HTMLTextAreaElement|null>(null);
  const [urlFragment, setUrlFragment] = useState(managedInteractive.url_fragment);
  const user = useCurrentUser();
  const { handleAuthoredStateChange, handleLinkedInteractivesChange } = useAuthoringPreview({
    interactive: managedInteractive,
    handleUpdateItemPreview,
    interactiveAuthoredStateRef,
    linkedInteractivesRef
  });

  const handleUrlFragmentBlur = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
    setUrlFragment(e.target.value);
  };

  const renderRequiredFields = () => {
    if (!libraryInteractive) {
      return undefined;
    }

    return <>
      <div className="interactiveItemID">
        InteractiveID: {managedInteractive.interactive_item_id}
      </div>
      <fieldset>
        <label htmlFor="name">Name</label>
        <input
          type="text"
          id="name"
          name="name"
          defaultValue={name}
        />
      </fieldset>

      <fieldset>
        <Checkbox
          id="is_half_width"
          name="is_half_width"
          defaultChecked={is_half_width}
          label="Half Width"
          inputNote="In full-width layout only."
        />
      </fieldset>
    </>;
  };

  const renderTabs = () => {
    if (!libraryInteractive) {
      return undefined;
    }

    const interactive = {
      url: `${libraryInteractive.base_url}${urlFragment || ""}`,
      aspect_ratio: managedInteractive.aspect_ratio,
      aspect_ratio_method: managedInteractive.inherit_aspect_ratio_method
        ? libraryInteractive.aspect_ratio_method
        : managedInteractive.custom_aspect_ratio_method,
      authored_state: managedInteractive.authored_state,
      interactive_item_id: managedInteractive.interactive_item_id,
      linked_interactives: managedInteractive.linked_interactives
    };

    const renderAuthoringPanel = () => {
      const { url_fragment } = managedInteractive;

      return (<>
        {libraryInteractive.authorable
          ? <InteractiveAuthoring
              interactive={interactive}
              onAuthoredStateChange={handleAuthoredStateChange}
              onLinkedInteractivesChange={handleLinkedInteractivesChange}
              allowReset={false}
              authoringApiUrls={authoringApiUrls}
            />
          : <>
              <fieldset>
                <legend>URL Fragment</legend>
                <textarea
                  id="url_fragment"
                  name="url_fragment"
                  defaultValue={url_fragment}
                  onBlur={handleUrlFragmentBlur}
                />
              </fieldset>
              {libraryInteractive.authoring_guidance
                ? <fieldset>
                    <legend>Authoring Guidance</legend>
                    <div dangerouslySetInnerHTML={{__html: libraryInteractive.authoring_guidance}} />
                  </fieldset>
                : undefined
              }
            </>
        }
      </>);
    };

    return (
      <Tabs>
        <TabList>
          <Tab>Authoring</Tab>
          <Tab>Advanced Options</Tab>
          {user?.isAdmin ? <Tab>Admin</Tab> : undefined}
        </TabList>
        <TabPanel forceRender={true}>
          {renderAuthoringPanel()}
        </TabPanel>
        <TabPanel forceRender={true}>
          <CustomizeManagedInteractive
            libraryInteractive={libraryInteractive}
            managedInteractive={managedInteractive}
            defaultClickToPlayPrompt={defaultClickToPlayPrompt}
          />
        </TabPanel>
        {user?.isAdmin
          ? <TabPanel forceRender={true}>
              <AuthoredState
                id="authored_state"
                name="authored_state"
                authoredState={interactive.authored_state}
              />
            </TabPanel>
          : undefined}
      </Tabs>
    );
  };

  // this generates a form element that renders inside the rails popup form
  return <>
      <input
        type="hidden"
        id="library_interactive_id"
        name="library_interactive_id"
        ref={libraryInteractiveIdRef}
        defaultValue={libraryInteractive ? libraryInteractive.id : ""}
      />
      <textarea
        id="authored_state"
        name="authored_state"
        ref={interactiveAuthoredStateRef}
        defaultValue={managedInteractive.authored_state}
        style={{ display: "none" }}
      />
      <textarea
        id="linked_interactives"
        name="linked_interactives"
        ref={linkedInteractivesRef}
        style={{ display: "none" }}
      />
    {renderRequiredFields()}
    {renderTabs()}
  </>;
};