New source found from dndbeyond.com

This commit is contained in:
2026-07-08 01:00:09 -07:00
parent 9a983a6d7b
commit dcefa0d2f2
2865 changed files with 222467 additions and 49053 deletions
@@ -2,8 +2,9 @@ import axios, { Canceler } from "axios";
import { orderBy } from "lodash";
import React, { FocusEvent, HTMLAttributes, useContext } from "react";
import { DispatchProp } from "react-redux";
import { AnyAction } from "redux";
import { LoadingPlaceholder, Select } from "@dndbeyond/character-components/es";
import { LoadingPlaceholder } from "@dndbeyond/character-components/es";
import {
AlignmentContract,
ApiAdapterPromise,
@@ -44,12 +45,16 @@ import {
rulesEngineSelectors,
serviceDataSelectors,
} from "@dndbeyond/character-rules-engine/es";
import MinusIcon from "@dndbeyond/fontawesome-cache/svgs/solid/minus.svg";
import PlusIcon from "@dndbeyond/fontawesome-cache/svgs/solid/plus.svg";
import { Accordion } from "~/components/Accordion";
import { Button } from "~/components/Button";
import { CollapsibleContent } from "~/components/CollapsibleContent";
import { HelperTextAccordion } from "~/components/HelperTextAccordion";
import { HtmlContent } from "~/components/HtmlContent";
import { Link } from "~/components/Link";
import { Select } from "~/components/Select";
import { useSource } from "~/hooks/useSource";
import { EditorWithDialog } from "~/subApps/builder/components/EditorWithDialog";
import { InputField } from "~/subApps/builder/components/InputField";
@@ -70,10 +75,8 @@ import {
appEnvSelectors,
} from "../../../../Shared/selectors";
import { AppLoggerUtils } from "../../../../Shared/utils";
import Button from "../../../components/Button";
import { GrantedFeat } from "../../../components/GrantedFeat/GrantedFeat";
import Page from "../../../components/Page";
import { PageBody } from "../../../components/PageBody";
import PageSubHeader from "../../../components/PageSubHeader";
import { BuilderAppState } from "../../../typings";
import ConnectedBuilderPage from "../ConnectedBuilderPage";
@@ -188,89 +191,67 @@ class CustomBackgroundManager extends React.PureComponent<
];
return (
<div className="custom-background">
<div className="custom-background-editor">
<div className="custom-background-properties">
<div className="custom-background-property custom-background-property-full">
<div className="custom-background-property-value">
<input
type="text"
defaultValue={name || ""}
onBlur={this.handleTextInputBlur.bind(this, "name")}
/>
</div>
<div className="custom-background-property-label">Name</div>
</div>
<div className="custom-background-property custom-background-property-textarea">
<div className="custom-background-property-value">
<textarea
defaultValue={description || ""}
onBlur={this.handleTextInputBlur.bind(this, "description")}
/>
</div>
<div className="custom-background-property-label">
Description
</div>
</div>
<div className="custom-background-property custom-background-property-choice">
<div className="custom-background-property-value">
<Select
onChange={this.handleNumberSelectChange.bind(
this,
"modifierType"
)}
options={modifierTypeOptions}
value={modifierType}
clsNames={[""]}
/>
</div>
<div className="custom-background-property-label">
Proficiency/Language Choices
</div>
</div>
<div className="custom-background-property custom-background-property-choice">
<div className="custom-background-property-value">
<Select
onChange={this.handleNumberSelectChange.bind(
this,
"featureId"
)}
options={featureOptions}
value={featureId}
clsNames={[""]}
/>
</div>
<div className="custom-background-property-label">
Background Feature
</div>
</div>
<div className="custom-background-property custom-background-property-choice">
<div className="custom-background-property-value">
<Select
onChange={this.handleNumberSelectChange.bind(
this,
"characteristicsId"
)}
options={characteristicsOptions}
value={characteristicsId}
clsNames={[""]}
/>
</div>
<div className="custom-background-property-label">
Background Characteristics
</div>
</div>
</div>
<div className="custom-background-actions">
<Button onClick={this.handleSave}>Save</Button>
</div>
<div className={styles.customBackground}>
<InputField
className={styles.customBackgroundField}
type="text"
label="Name"
defaultValue={name || ""}
onBlur={this.handleTextInputBlur.bind(this, "name")}
/>
<InputField
className={styles.customBackgroundField}
type="textarea"
label="Description"
defaultValue={description || ""}
onBlur={this.handleTextInputBlur.bind(this, "description")}
/>
<label className={styles.customBackgroundLabel}>
Proficiency/Language Choices
</label>
<Select
className={styles.customBackgroundField}
onChange={this.handleNumberSelectChange.bind(this, "modifierType")}
options={modifierTypeOptions}
value={modifierType}
name="custom-background-description"
/>
<label className={styles.customBackgroundLabel}>
Background Feature
</label>
<Select
className={styles.customBackgroundField}
onChange={this.handleNumberSelectChange.bind(this, "featureId")}
options={featureOptions}
value={featureId}
name="custom-background-feature"
/>
<label className={styles.customBackgroundLabel}>
Background Characteristics
</label>
<Select
className={styles.customBackgroundField}
onChange={this.handleNumberSelectChange.bind(
this,
"characteristicsId"
)}
options={characteristicsOptions}
value={characteristicsId}
name="custom-background-characteristics"
/>
<div className={styles.customBackgroundActions}>
<Button variant="builder" size="x-small" onClick={this.handleSave}>
Save
</Button>
</div>
</div>
);
}
}
interface Props extends DispatchProp {
// TODO: We are typing with AnyAction here to work around issues with UnknownAction, the type that was introduced in Redux 5.
// If we decide to modernize to Redux 5, we will need to refactor much of the Rules Engine redux usage.
interface Props extends DispatchProp<AnyAction> {
ruleData: RuleData;
background: Background | null;
currentBackground: BackgroundContract | null;
@@ -719,6 +700,7 @@ class DescriptionManage extends React.PureComponent<Props, State> {
options={availableOptions}
onChange={this.handleChoiceChange}
choiceInfo={choiceInfo}
className={styles.backgroundChoiceSelect}
/>
);
})}
@@ -1105,23 +1087,24 @@ class DescriptionManage extends React.PureComponent<Props, State> {
{description}
</div>
)}
<div className={styles.accordionGroup}>
{this.renderBackgroundChoices(background)}
{featuresBackground &&
this.renderBackgroundFeature(featuresBackground, [
featuresBackground.name ? featuresBackground.name : "",
])}
{this.renderBackgroundChoices(background)}
{featuresBackground &&
this.renderBackgroundFeature(featuresBackground, [
featuresBackground.name ? featuresBackground.name : "",
])}
{characteristicsBackground &&
this.hasCharacteristics(characteristicsBackground) && (
<Accordion
summary="Suggested Characteristics"
summaryMetaItems={metaItems}
variant="paper"
>
{this.renderBackgroundSuggestionsUi(characteristicsBackground)}
</Accordion>
)}
{characteristicsBackground &&
this.hasCharacteristics(characteristicsBackground) && (
<Accordion
summary="Suggested Characteristics"
summaryMetaItems={metaItems}
variant="paper"
>
{this.renderBackgroundSuggestionsUi(characteristicsBackground)}
</Accordion>
)}
</div>
</div>
);
};
@@ -1130,15 +1113,15 @@ class DescriptionManage extends React.PureComponent<Props, State> {
const { customizeCollapsed } = this.state;
const { background, getGroupedOptionsBySourceCategory } = this.props;
const backgroundOptions: Array<HtmlSelectOption | HtmlSelectOptionGroup> = [
const backgroundOptions = [
{
label: "Custom Background",
value: -1,
},
{
label: "---------",
disabled: true,
value: "-----",
optGroupLabel: "---------",
options: [
{
label: "Custom Background",
value: -1,
},
],
},
...getGroupedOptionsBySourceCategory(this.getBackgroundData()),
];
@@ -1152,15 +1135,8 @@ class DescriptionManage extends React.PureComponent<Props, State> {
}
}
let clsNames: string[] = ["custom-background-customize-header"];
if (customizeCollapsed) {
clsNames.push("custom-background-customize-header-closed");
} else {
clsNames.push("custom-background-customize-header-opened");
}
return (
<React.Fragment>
<>
<div className="ct-character-tools__marketplace-callout">
Check your source settings on the <strong>Home</strong> tab if you
can't find Backgrounds you've purchased.
@@ -1169,27 +1145,24 @@ class DescriptionManage extends React.PureComponent<Props, State> {
<Link href="/marketplace">Marketplace</Link> for more Background
options.
</div>
<div className="description-manage-background-chooser-con">
<div className="description-manage-background-chooser-field">
<Select
onChangePromise={this.handleBackgroundChangePromise}
options={backgroundOptions}
value={backgroundId}
clsNames={["description-manage-background-chooser"]}
/>
</div>
<div className={styles.backgroundSelectContainer}>
<Select
className={styles.backgroundSelect}
options={backgroundOptions}
onChangeConfirm={this.handleBackgroundChangePromise}
value={backgroundId}
name="background-chooser"
/>
{background && background.hasCustomBackground && (
<div className="description-manage-background-chooser-extra">
<div
className={clsNames.join(" ")}
onClick={this.handleConfigureClick}
>
<div className="custom-background-customize-heading">
Configure
</div>
<div className="custom-background-customize-trigger" />
</div>
</div>
<Button
className={styles.customBackgroundToggle}
variant="builder-text"
size="x-small"
onClick={this.handleConfigureClick}
>
Configure
{customizeCollapsed ? <PlusIcon /> : <MinusIcon />}
</Button>
)}
</div>
{background &&
@@ -1198,7 +1171,7 @@ class DescriptionManage extends React.PureComponent<Props, State> {
{background &&
background.hasCustomBackground &&
this.renderCustomBackgroundUI()}
</React.Fragment>
</>
);
};
@@ -1293,7 +1266,7 @@ class DescriptionManage extends React.PureComponent<Props, State> {
lifestyle.cost === "-" ? "" : `(${lifestyle.cost})`
}`,
value: lifestyle.id,
}));
})) as HtmlSelectOption[];
const numberInputAttributes = {
min: CHARACTER_DESCRIPTION_NUMBER_VALUE.MIN,
@@ -1302,283 +1275,279 @@ class DescriptionManage extends React.PureComponent<Props, State> {
return (
<Page clsNames={["description-manage"]}>
<PageBody>
<div className="description-manage-information">
{this.renderInformationCollapsible()}
<div className="description-manage-information">
{this.renderInformationCollapsible()}
</div>
{this.renderBackgroundUi()}
<Accordion
summary="Character Details"
summaryMetaItems={["Alignment", "Faith", "Lifestyle"]}
variant="paper"
>
<div className="description-manage-alignment">
<label className="description-manage-alignment-label">
Alignment
</label>
<Select
options={alignmentOptions}
value={alignment === null ? null : alignment.id}
onChange={this.handleAlignmentChange}
name="alignment-select"
/>
{alignmentDescriptionNode}
</div>
{this.renderBackgroundUi()}
<Accordion
summary="Character Details"
summaryMetaItems={["Alignment", "Faith", "Lifestyle"]}
variant="paper"
>
<div className="description-manage-alignment">
<label className="description-manage-alignment-label">
Alignment
</label>
<Select
options={alignmentOptions}
value={alignment === null ? null : alignment.id}
onChange={this.handleAlignmentChange}
/>
{alignmentDescriptionNode}
</div>
<div className="description-manage-faith">
<InputField
label="Faith"
initialValue={faith}
onBlur={this.handleFaithChange}
maxLength={512}
inputProps={
{
spellCheck: false,
autoComplete: "off",
} as HTMLAttributes<HTMLInputElement>
}
/>
</div>
<div className="description-manage-lifestyle">
<label className="description-manage-lifestyle-label">
Lifestyle
</label>
<Select
options={lifestyleOptions}
value={lifestyle === null ? null : lifestyle.id}
onChange={this.handleLifestyleChange}
/>
{lifestyle !== null && (
<CollapsibleContent className="description-manage-lifestyle-desc">
{lifestyle.description ? lifestyle.description : ""}
</CollapsibleContent>
)}
</div>
</Accordion>
<Accordion
summary="Physical Characteristics"
summaryMetaItems={[
"Hair",
"Skin",
"Eyes",
"Height",
"Weight",
"Age",
"Gender",
]}
variant="paper"
>
<div className="description-manage-faith">
<InputField
className={styles.inputField}
label="Hair"
initialValue={hair}
onBlur={this.handleHairChange}
maxLength={50}
/>
<InputField
className={styles.inputField}
label="Skin"
initialValue={skin}
onBlur={this.handleSkinChange}
maxLength={50}
/>
<InputField
className={styles.inputField}
label="Eyes"
initialValue={eyes}
onBlur={this.handleEyesChange}
maxLength={50}
/>
<InputField
className={styles.inputField}
label="Height"
initialValue={height}
onBlur={this.handleHeightChange}
maxLength={50}
/>
<InputField
className={styles.inputField}
label="Weight (lbs)"
type="number"
initialValue={weight}
onBlur={this.handleWeightChange}
inputProps={numberInputAttributes}
/>
<InputField
className={styles.inputField}
label="Age (Years)"
type="number"
initialValue={age}
onBlur={this.handleAgeChange}
inputProps={numberInputAttributes}
/>
<InputField
className={styles.inputField}
label="Gender"
initialValue={gender}
onBlur={this.handleGenderChange}
maxLength={128}
/>
</Accordion>
<Accordion
summary="Personal Characteristics"
summaryMetaItems={["Personality", "Ideals", "Bonds", "Flaws"]}
variant="paper"
>
<EditorWithDialog
heading={<h3>Personality Traits</h3>}
editButtonLabel="Edit Traits"
addButtonLabel="Add Traits"
placeholder="Add traits here..."
content={this.getTraitContent(
Constants.TraitTypeEnum.PERSONALITY_TRAITS
)}
onSave={this.handleSaveTrait.bind(
this,
Constants.TraitTypeEnum.PERSONALITY_TRAITS
)}
extraNode={this.renderTextareaSuggestionList(
"Personality Traits",
"personalityTraits",
"Choose Two"
)}
/>
<EditorWithDialog
heading={<h3>Ideals</h3>}
editButtonLabel="Edit Ideals"
addButtonLabel="Add Ideals"
placeholder="Add ideals here..."
content={this.getTraitContent(Constants.TraitTypeEnum.IDEALS)}
onSave={this.handleSaveTrait.bind(
this,
Constants.TraitTypeEnum.IDEALS
)}
extraNode={this.renderTextareaSuggestionList("Ideals", "ideals")}
/>
<EditorWithDialog
heading={<h3>Bonds</h3>}
editButtonLabel="Edit Bonds"
addButtonLabel="Add Bonds"
placeholder="Add bonds here..."
content={this.getTraitContent(Constants.TraitTypeEnum.BONDS)}
onSave={this.handleSaveTrait.bind(
this,
Constants.TraitTypeEnum.BONDS
)}
extraNode={this.renderTextareaSuggestionList("Bonds", "bonds")}
/>
<EditorWithDialog
heading={<h3>Flaws</h3>}
editButtonLabel="Edit Flaws"
addButtonLabel="Add Flaws"
placeholder="Add flaws here..."
content={this.getTraitContent(Constants.TraitTypeEnum.FLAWS)}
onSave={this.handleSaveTrait.bind(
this,
Constants.TraitTypeEnum.FLAWS
)}
extraNode={this.renderTextareaSuggestionList("Flaws", "flaws")}
/>
</Accordion>
<Accordion
summary="Notes"
summaryMetaItems={[
"Organizations",
"Allies",
"Enemies",
"Backstory",
"Other",
]}
variant="paper"
>
<EditorWithDialog
heading={<h3>Organizations</h3>}
editButtonLabel="Edit Notes"
addButtonLabel="Add Notes"
placeholder="Add organization notes here..."
content={this.getNoteContent(Constants.NoteKeyEnum.ORGANIZATIONS)}
onSave={this.handleSaveNote.bind(
this,
Constants.NoteKeyEnum.ORGANIZATIONS
)}
extraNode={
<p className="description-suggestion-info">
Are there any important organizations your character belongs
to? Any societies, orders, cults, or agencies?
</p>
label="Faith"
initialValue={faith}
onBlur={this.handleFaithChange}
maxLength={512}
inputProps={
{
spellCheck: false,
autoComplete: "off",
} as HTMLAttributes<HTMLInputElement>
}
/>
<EditorWithDialog
heading={<h3>Allies</h3>}
editButtonLabel="Edit Notes"
addButtonLabel="Add Notes"
placeholder="Add allies here..."
content={this.getNoteContent(Constants.NoteKeyEnum.ALLIES)}
onSave={this.handleSaveNote.bind(
this,
Constants.NoteKeyEnum.ALLIES
)}
extraNode={
<p className="description-suggestion-info">
With whom does your character associate? When in need of aid,
do they seek out their kinfolk, followers of their deity, or
other members of their order? What groups or individuals are
they aligned with?
</p>
}
</div>
<div className="description-manage-lifestyle">
<label className="description-manage-lifestyle-label">
Lifestyle
</label>
<Select
options={lifestyleOptions}
value={lifestyle === null ? null : lifestyle.id}
onChange={this.handleLifestyleChange}
name="lifestyle-select"
/>
<EditorWithDialog
heading={<h3>Enemies</h3>}
editButtonLabel="Edit Notes"
addButtonLabel="Add Notes"
placeholder="Add enemies here..."
content={this.getNoteContent(Constants.NoteKeyEnum.ENEMIES)}
onSave={this.handleSaveNote.bind(
this,
Constants.NoteKeyEnum.ENEMIES
)}
extraNode={
<p className="description-suggestion-info">
Who does your character fear or fight? Have they sworn a vow
to rid the world of undead? Is there an order or organization
they are opposed to? Are there any specific foes from their
past?
</p>
}
/>
<EditorWithDialog
heading={<h3>Backstory</h3>}
editButtonLabel="Edit Notes"
addButtonLabel="Add Notes"
placeholder="Add a backstory..."
content={this.getNoteContent(Constants.NoteKeyEnum.BACKSTORY)}
onSave={this.handleSaveNote.bind(
this,
Constants.NoteKeyEnum.BACKSTORY
)}
extraNode={
<p className="description-suggestion-info">
Talk about your character's origins. Where are they from? How
did they end up adventuring? How did they choose their class?
</p>
}
/>
<EditorWithDialog
heading={<h3>Other</h3>}
editButtonLabel="Edit Notes"
addButtonLabel="Add Notes"
placeholder="Add additional notes here..."
content={this.getNoteContent(Constants.NoteKeyEnum.OTHER)}
onSave={this.handleSaveNote.bind(
this,
Constants.NoteKeyEnum.OTHER
)}
extraNode={
<p className="description-suggestion-info">
Anything at all you'd like to mention about your character.
</p>
}
/>
</Accordion>
</PageBody>
{lifestyle !== null && (
<CollapsibleContent className="description-manage-lifestyle-desc">
{lifestyle.description ? lifestyle.description : ""}
</CollapsibleContent>
)}
</div>
</Accordion>
<Accordion
summary="Physical Characteristics"
summaryMetaItems={[
"Hair",
"Skin",
"Eyes",
"Height",
"Weight",
"Age",
"Gender",
]}
variant="paper"
>
<InputField
className={styles.inputField}
label="Hair"
initialValue={hair}
onBlur={this.handleHairChange}
maxLength={50}
/>
<InputField
className={styles.inputField}
label="Skin"
initialValue={skin}
onBlur={this.handleSkinChange}
maxLength={50}
/>
<InputField
className={styles.inputField}
label="Eyes"
initialValue={eyes}
onBlur={this.handleEyesChange}
maxLength={50}
/>
<InputField
className={styles.inputField}
label="Height"
initialValue={height}
onBlur={this.handleHeightChange}
maxLength={50}
/>
<InputField
className={styles.inputField}
label="Weight (lbs)"
type="number"
initialValue={weight}
onBlur={this.handleWeightChange}
inputProps={numberInputAttributes}
/>
<InputField
className={styles.inputField}
label="Age (Years)"
type="number"
initialValue={age}
onBlur={this.handleAgeChange}
inputProps={numberInputAttributes}
/>
<InputField
className={styles.inputField}
label="Gender"
initialValue={gender}
onBlur={this.handleGenderChange}
maxLength={128}
/>
</Accordion>
<Accordion
summary="Personal Characteristics"
summaryMetaItems={["Personality", "Ideals", "Bonds", "Flaws"]}
variant="paper"
>
<EditorWithDialog
heading={<h3>Personality Traits</h3>}
editButtonLabel="Edit Traits"
addButtonLabel="Add Traits"
placeholder="Add traits here..."
content={this.getTraitContent(
Constants.TraitTypeEnum.PERSONALITY_TRAITS
)}
onSave={this.handleSaveTrait.bind(
this,
Constants.TraitTypeEnum.PERSONALITY_TRAITS
)}
extraNode={this.renderTextareaSuggestionList(
"Personality Traits",
"personalityTraits",
"Choose Two"
)}
/>
<EditorWithDialog
heading={<h3>Ideals</h3>}
editButtonLabel="Edit Ideals"
addButtonLabel="Add Ideals"
placeholder="Add ideals here..."
content={this.getTraitContent(Constants.TraitTypeEnum.IDEALS)}
onSave={this.handleSaveTrait.bind(
this,
Constants.TraitTypeEnum.IDEALS
)}
extraNode={this.renderTextareaSuggestionList("Ideals", "ideals")}
/>
<EditorWithDialog
heading={<h3>Bonds</h3>}
editButtonLabel="Edit Bonds"
addButtonLabel="Add Bonds"
placeholder="Add bonds here..."
content={this.getTraitContent(Constants.TraitTypeEnum.BONDS)}
onSave={this.handleSaveTrait.bind(
this,
Constants.TraitTypeEnum.BONDS
)}
extraNode={this.renderTextareaSuggestionList("Bonds", "bonds")}
/>
<EditorWithDialog
heading={<h3>Flaws</h3>}
editButtonLabel="Edit Flaws"
addButtonLabel="Add Flaws"
placeholder="Add flaws here..."
content={this.getTraitContent(Constants.TraitTypeEnum.FLAWS)}
onSave={this.handleSaveTrait.bind(
this,
Constants.TraitTypeEnum.FLAWS
)}
extraNode={this.renderTextareaSuggestionList("Flaws", "flaws")}
/>
</Accordion>
<Accordion
summary="Notes"
summaryMetaItems={[
"Organizations",
"Allies",
"Enemies",
"Backstory",
"Other",
]}
variant="paper"
>
<EditorWithDialog
heading={<h3>Organizations</h3>}
editButtonLabel="Edit Notes"
addButtonLabel="Add Notes"
placeholder="Add organization notes here..."
content={this.getNoteContent(Constants.NoteKeyEnum.ORGANIZATIONS)}
onSave={this.handleSaveNote.bind(
this,
Constants.NoteKeyEnum.ORGANIZATIONS
)}
extraNode={
<p className="description-suggestion-info">
Are there any important organizations your character belongs to?
Any societies, orders, cults, or agencies?
</p>
}
/>
<EditorWithDialog
heading={<h3>Allies</h3>}
editButtonLabel="Edit Notes"
addButtonLabel="Add Notes"
placeholder="Add allies here..."
content={this.getNoteContent(Constants.NoteKeyEnum.ALLIES)}
onSave={this.handleSaveNote.bind(
this,
Constants.NoteKeyEnum.ALLIES
)}
extraNode={
<p className="description-suggestion-info">
With whom does your character associate? When in need of aid, do
they seek out their kinfolk, followers of their deity, or other
members of their order? What groups or individuals are they
aligned with?
</p>
}
/>
<EditorWithDialog
heading={<h3>Enemies</h3>}
editButtonLabel="Edit Notes"
addButtonLabel="Add Notes"
placeholder="Add enemies here..."
content={this.getNoteContent(Constants.NoteKeyEnum.ENEMIES)}
onSave={this.handleSaveNote.bind(
this,
Constants.NoteKeyEnum.ENEMIES
)}
extraNode={
<p className="description-suggestion-info">
Who does your character fear or fight? Have they sworn a vow to
rid the world of undead? Is there an order or organization they
are opposed to? Are there any specific foes from their past?
</p>
}
/>
<EditorWithDialog
heading={<h3>Backstory</h3>}
editButtonLabel="Edit Notes"
addButtonLabel="Add Notes"
placeholder="Add a backstory..."
content={this.getNoteContent(Constants.NoteKeyEnum.BACKSTORY)}
onSave={this.handleSaveNote.bind(
this,
Constants.NoteKeyEnum.BACKSTORY
)}
extraNode={
<p className="description-suggestion-info">
Talk about your character's origins. Where are they from? How
did they end up adventuring? How did they choose their class?
</p>
}
/>
<EditorWithDialog
heading={<h3>Other</h3>}
editButtonLabel="Edit Notes"
addButtonLabel="Add Notes"
placeholder="Add additional notes here..."
content={this.getNoteContent(Constants.NoteKeyEnum.OTHER)}
onSave={this.handleSaveNote.bind(this, Constants.NoteKeyEnum.OTHER)}
extraNode={
<p className="description-suggestion-info">
Anything at all you'd like to mention about your character.
</p>
}
/>
</Accordion>
</Page>
);
}