Skip to content

Commit

Permalink
Add option to change vernacular language name in Project Settings (#2528
Browse files Browse the repository at this point in the history
)

* Add edit button for vernacular language

* Simplify edit vernacular language name

* Improve Vernacular Name save behavior and improve Project Name save behavior

* Fix ProjectName tests for new save behavior

* Fix english translation
  • Loading branch information
Apoktieno authored Sep 1, 2023
1 parent 7306475 commit be7cfe7
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 6 deletions.
2 changes: 2 additions & 0 deletions public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,12 @@
"bcp47": "BCP 47 Code",
"name": "Name",
"font": "Font",
"changeName": "Change Name",
"addAnalysisLanguage": "Add an alternate analysis language",
"makeDefaultAnalysisLanguage": "Make this the default analysis language",
"deleteAnalysisLanguage": "Delete this analysis language",
"getGlossLanguages": "Find all language codes used in the current data",
"updateVernacularLanguageNameFailed": "Failed to update vernacular language name",
"addAnalysisLanguageFailed": "Failed to add analysis language",
"makeDefaultAnalysisLanguageFailed": "Failed to make default analysis language",
"deleteAnalysisLanguageFailed": "Failed to delete analysis language",
Expand Down
82 changes: 80 additions & 2 deletions src/components/ProjectSettings/ProjectLanguages.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import {
Add,
ArrowUpward,
BorderColor,
Clear,
Delete,
Done,
Search,
} from "@mui/icons-material";
import {
Button,
Grid,
IconButton,
MenuItem,
Select,
SelectChangeEvent,
TextField,
Typography,
} from "@mui/material";
import { LanguagePicker, languagePickerStrings_en } from "mui-language-picker";
Expand All @@ -37,9 +40,11 @@ export default function ProjectLanguages(
props: ProjectSettingPropsWithUpdate
): ReactElement {
const [add, setAdd] = useState(false);
const [changeVernName, setChangeVernName] = useState(false);
const [isNewLang, setIsNewLang] = useState(false);
const [langsInProj, setLangsInProj] = useState("");
const [newLang, setNewLang] = useState(newWritingSystem());
const [newVernName, setNewVernName] = useState("");
const { t } = useTranslation();

useEffect(() => {
Expand All @@ -49,7 +54,12 @@ export default function ProjectLanguages(
.map((ws) => ws.bcp47)
.includes(newLang.bcp47)
);
}, [newLang.bcp47, props.project.analysisWritingSystems]);
setNewVernName(props.project.vernacularWritingSystem.name);
}, [
newLang.bcp47,
props.project.analysisWritingSystems,
props.project.vernacularWritingSystem.name,
]);

const setNewAnalysisDefault = async (index: number): Promise<void> => {
const analysisWritingSystems = [...props.project.analysisWritingSystems];
Expand Down Expand Up @@ -145,6 +155,25 @@ export default function ProjectLanguages(
setNewLang(newWritingSystem());
};

const updateVernacularName = async (): Promise<void> => {
if (newVernName != props.project.vernacularWritingSystem.name) {
var vernacularWritingSystem = props.project.vernacularWritingSystem;
vernacularWritingSystem.name = newVernName;
await props
.updateProject({
...props.project,
vernacularWritingSystem,
})
.then(() => resetState())
.catch((err) => {
console.error(err);
toast.error(
t("projectSettings.language.updateVernacularLanguageNameFailed")
);
});
}
};

const addAnalysisLangButtons = (): ReactElement => (
<>
<IconButtonWithTooltip
Expand Down Expand Up @@ -238,13 +267,62 @@ export default function ProjectLanguages(
</Select>
);

const vernacularLanguageDisplay = (): ReactElement => (
<ImmutableWritingSystem
ws={props.project.vernacularWritingSystem}
buttons={
<IconButtonWithTooltip
icon={<BorderColor fontSize="inherit" />}
textId={t("projectSettings.language.changeName")}
small
onClick={() => {
setChangeVernName(true);
}}
buttonId={`vernacular-language-edit`}
/>
}
/>
);

const vernacularLanguageEditor = (): ReactElement => (
<Grid container spacing={1}>
<Grid item xs={12}>
<TextField
variant="standard"
id="vernacular-name"
value={newVernName}
onChange={(e) => setNewVernName(e.target.value)}
onBlur={() => {
setChangeVernName(false);
setNewVernName(props.project.vernacularWritingSystem.name);
}}
autoFocus
/>
</Grid>
<Grid item>
<Button
variant="contained"
color="primary"
id="vernacular-name-save"
onMouseDown={() => {
updateVernacularName();
}}
>
{t("buttons.save")}
</Button>
</Grid>
</Grid>
);

return (
<>
{/* Vernacular language */}
<Typography variant="h6">
{t("projectSettings.language.vernacular")}
</Typography>
<ImmutableWritingSystem ws={props.project.vernacularWritingSystem} />
{changeVernName
? vernacularLanguageEditor()
: vernacularLanguageDisplay()}

{/* Analysis languages */}
<Typography style={{ marginTop: theme.spacing(1) }} variant="h6">
Expand Down
8 changes: 7 additions & 1 deletion src/components/ProjectSettings/ProjectName.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default function ProjectName(
id="project-name"
value={projName}
onChange={(e) => setProjName(e.target.value)}
onBlur={() => updateProjectName()}
onBlur={() => setProjName(props.project.name)}
/>
</Grid>
<Grid item>
Expand All @@ -42,6 +42,12 @@ export default function ProjectName(
variant="contained"
color="primary"
id="project-name-save"
onMouseDown={(e) => {
e.preventDefault();
}}
onClick={() => {
updateProjectName();
}}
>
{t("buttons.save")}
</Button>
Expand Down
8 changes: 5 additions & 3 deletions src/components/ProjectSettings/tests/ProjectName.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TextField } from "@mui/material";
import { Button, TextField } from "@mui/material";
import renderer from "react-test-renderer";

import "tests/reactI18nextMock";
Expand Down Expand Up @@ -30,24 +30,26 @@ describe("ProjectName", () => {
it("updates project name", async () => {
await renderName();
const textField = testRenderer.root.findByType(TextField);
const saveButton = testRenderer.root.findByType(Button);
const name = "new-project-name";
mockUpdateProject.mockResolvedValueOnce({});
await renderer.act(async () =>
textField.props.onChange({ target: { value: name } })
);
await renderer.act(async () => textField.props.onBlur());
await renderer.act(async () => saveButton.props.onClick());
expect(mockUpdateProject).toBeCalledWith({ ...mockProject, name });
});

it("toasts on error", async () => {
await renderName();
const textField = testRenderer.root.findByType(TextField);
const saveButton = testRenderer.root.findByType(Button);
await renderer.act(async () =>
textField.props.onChange({ target: { value: "new-name" } })
);
mockUpdateProject.mockRejectedValueOnce({});
expect(mockToastError).not.toBeCalled();
await renderer.act(async () => textField.props.onBlur());
await renderer.act(async () => saveButton.props.onClick());
expect(mockToastError).toBeCalledTimes(1);
});
});

0 comments on commit be7cfe7

Please sign in to comment.