diff --git a/src/components/ProjectSettings/ProjectLanguages/ProjectLanguages.tsx b/src/components/ProjectSettings/ProjectLanguages/ProjectLanguages.tsx index 9f6524f757..69e348784a 100644 --- a/src/components/ProjectSettings/ProjectLanguages/ProjectLanguages.tsx +++ b/src/components/ProjectSettings/ProjectLanguages/ProjectLanguages.tsx @@ -1,11 +1,10 @@ -import { Grid, Typography } from "@material-ui/core"; +import { Button, Grid, Tooltip, Typography } from "@material-ui/core"; +import { Add, ArrowUpward, Clear, Done } from "@material-ui/icons"; +import { LanguagePicker, languagePickerStrings_en } from "mui-language-picker"; import React from "react"; -import { - LocalizeContextProps, - Translate, - withLocalize, -} from "react-localize-redux"; +import { Translate } from "react-localize-redux"; +import { updateProject } from "../../../backend"; import { Project, WritingSystem } from "../../../types/project"; import theme from "../../../types/theme"; @@ -13,54 +12,184 @@ interface LanguageProps { project: Project; } -class ProjectLanguages extends React.Component< - LanguageProps & LocalizeContextProps +interface LanguageState { + add: boolean; + name: string; + bcp47: string; + font: string; +} + +export default class ProjectLanguages extends React.Component< + LanguageProps, + LanguageState > { - renderWritingSystem(ws: WritingSystem, index?: number) { + constructor(props: LanguageProps) { + super(props); + this.state = this.defaultState; + } + + private defaultState: LanguageState = { + add: false, + name: "", + bcp47: "", + font: "", + }; + + setNewAnalysisDefault(index: number) { + const newDefault = this.props.project.analysisWritingSystems.splice( + index, + 1 + )[0]; + this.props.project.analysisWritingSystems.splice(0, 0, newDefault); + updateProject(this.props.project) + .then(() => this.resetState()) + .catch((err) => console.error(err)); + } + + addAnalysisWritingSystem() { + const ws = { + name: this.state.name, + bcp47: this.state.bcp47, + font: this.state.font, + }; + this.props.project.analysisWritingSystems.push(ws); + updateProject(this.props.project) + .then(() => this.resetState()) + .catch((err) => console.error(err)); + } + + isNewWritingSystem() { return ( - - - {index !== undefined && {`${index + 1}. `}} - - - {": "} - {ws.name} {", "} - - - - {": "} - {ws.bcp47} - {", "} - - - - {": "} - {ws.font} - - - + this.state.bcp47 && + !this.props.project.analysisWritingSystems + .map((ws) => ws.bcp47) + .includes(this.state.bcp47) ); } + resetState() { + this.setState(this.defaultState); + } + render() { return ( {": "} - {this.renderWritingSystem(this.props.project.vernacularWritingSystem)} + {": "} {this.props.project.analysisWritingSystems.map( - (writingSystem, index) => - this.renderWritingSystem(writingSystem, index) + (writingSystem, index) => ( + } + iconAction={() => this.setNewAnalysisDefault(index)} + /> + ) )} + {this.state.add ? ( + + + this.setState({ bcp47 })} + name={this.state.bcp47} + setName={(name: string) => this.setState({ name })} + font={this.state.font} + setFont={(font: string) => this.setState({ font })} + t={languagePickerStrings_en} + /> + {" "} + + + {" "} + + + + + ) : ( + + } + placement="right" + > + + + )} ); } } -export default withLocalize(ProjectLanguages); +function MakeDefaultButton() { + return ( + + } + placement="right" + > + + + ); +} + +interface ImmutableWritingSystemProps { + ws: WritingSystem; + index?: number; + icon?: any; + iconAction?: () => void; +} + +export function ImmutableWritingSystem(props: ImmutableWritingSystemProps) { + return ( + + {props.index !== undefined && {`${props.index + 1}. `}} + + + {": "} + {props.ws.name} {", "} + + + + {": "} + {props.ws.bcp47} + {", "} + + + + {": "} + {props.ws.font} + + {props.icon ? ( + + + + ) : null} + + ); +} diff --git a/src/components/ProjectSettings/ProjectLanguages/tests/ProjectLangauges.test.tsx b/src/components/ProjectSettings/ProjectLanguages/tests/ProjectLangauges.test.tsx index 88fdc79a2e..2b8b071b20 100644 --- a/src/components/ProjectSettings/ProjectLanguages/tests/ProjectLangauges.test.tsx +++ b/src/components/ProjectSettings/ProjectLanguages/tests/ProjectLangauges.test.tsx @@ -1,29 +1,86 @@ +import { LanguagePicker } from "mui-language-picker"; import React from "react"; import { Provider } from "react-redux"; -import renderer from "react-test-renderer"; +import renderer, { + ReactTestInstance, + ReactTestRenderer, +} from "react-test-renderer"; import configureMockStore from "redux-mock-store"; -import { defaultProject, Project } from "../../../../types/project"; +import { + defaultProject, + Project, + WritingSystem, +} from "../../../../types/project"; import { defaultState } from "../../../App/DefaultState"; import ProjectLanguages from "../ProjectLanguages"; +jest.mock("../../../../backend", () => { + return { updateProject: (proj: Project) => mockUpdateProject(proj) }; +}); + const createMockStore = configureMockStore([]); const mockStore = createMockStore(defaultState); -let mockProject: Project = { - ...defaultProject, - analysisWritingSystems: [ - { name: "a", bcp47: "a", font: "" }, - { name: "b", bcp47: "b", font: "" }, - ], -}; -describe("Testing login component", () => { - it("Renders without crashing", () => { - renderer.act(() => { - renderer.create( - - - - ); - }); +const mockAnalysisWritingSystems: WritingSystem[] = [ + { name: "a", bcp47: "a", font: "" }, + { name: "b", bcp47: "b", font: "" }, +]; +const mockUpdateProject = jest.fn((proj: Project) => { + return Promise.resolve(proj); +}); + +let projectMaster: ReactTestRenderer; +let pickerHandle: ReactTestInstance; +let buttonHandle: ReactTestInstance; + +function mockProject(systems?: WritingSystem[]) { + return { ...defaultProject, analysisWritingSystems: systems ?? [] }; +} + +function renderProjLangs(proj: Project) { + renderer.act(() => { + projectMaster = renderer.create( + + + + ); + }); +} + +function renderAndClickAdd() { + renderProjLangs(mockProject([...mockAnalysisWritingSystems])); + expect(projectMaster.root.findAllByType(LanguagePicker).length).toEqual(0); + projectMaster.root.findByProps({ id: "addNewLang" }).props.onClick(); + expect(projectMaster.root.findAllByType(LanguagePicker).length).toEqual(1); +} + +describe("ProjectLanguages", () => { + it("renders without crashing", () => { + renderProjLangs(mockProject([...mockAnalysisWritingSystems])); + }); + + it("can add language to project", () => { + renderAndClickAdd(); + pickerHandle = projectMaster.root.findByType(LanguagePicker); + pickerHandle.props.setCode("z"); + pickerHandle.props.setName("z"); + projectMaster.root.findByProps({ id: "submitNewLang" }).props.onClick(); + expect(mockUpdateProject).toBeCalledWith( + mockProject([ + ...mockAnalysisWritingSystems, + { name: "z", bcp47: "z", font: "" }, + ]) + ); + }); + + it("can only submit when new language selected", () => { + renderAndClickAdd(); + pickerHandle = projectMaster.root.findByType(LanguagePicker); + buttonHandle = projectMaster.root.findByProps({ id: "submitNewLang" }); + expect(buttonHandle.props.disabled).toBe(true); + pickerHandle.props.setCode(mockAnalysisWritingSystems[0].bcp47); + expect(buttonHandle.props.disabled).toBe(true); + pickerHandle.props.setCode("z"); + expect(buttonHandle.props.disabled).toBe(false); }); }); diff --git a/src/resources/translations.json b/src/resources/translations.json index ad7645ac93..8df50a0896 100644 --- a/src/resources/translations.json +++ b/src/resources/translations.json @@ -295,7 +295,17 @@ ], "bcp47": ["BCP 47 Code", "BCP 47", "BCP 47"], "name": ["Name", "Nombre", "Nom"], - "font": ["Font", "Font", "Font"] + "font": ["Font", "Font", "Font"], + "addAnalysisLanguage": [ + "Add an alternate analysis language.", + "Add an alternate analysis language.", + "Add an alternate analysis language." + ], + "makeDefaultAnalysisLanguage": [ + "Make this the default analysis language.", + "Make this the default analysis language.", + "Make this the default analysis language." + ] }, "name": ["Project Name", "Nombre del proyecto", "Nom du projet"], "projectList": ["Projects", "Proyectos", "Projets"],