Skip to content

Commit

Permalink
[Bug #484] Ensure localized vocabulary label is used as option label …
Browse files Browse the repository at this point in the history
…in ImportedVocabulariesListEdit.
  • Loading branch information
ledsoft committed Jul 23, 2024
1 parent 183aa45 commit e871a48
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 80 deletions.
66 changes: 32 additions & 34 deletions src/component/vocabulary/ImportedVocabulariesListEdit.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,48 @@
import * as React from "react";
import { injectIntl } from "react-intl";
// @ts-ignore
import { IntelligentTreeSelect } from "intelligent-tree-select";
import withI18n, { HasI18n } from "../hoc/withI18n";
import Vocabulary from "../../model/Vocabulary";
import { AssetData } from "../../model/Asset";
import { Col, FormGroup, Label, Row } from "reactstrap";
import { connect } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import TermItState from "../../model/TermItState";
import Utils from "../../util/Utils";
import { createVocabularyValueRenderer } from "../misc/treeselect/Renderers";
import { ThunkDispatch } from "../../util/Types";
import { loadVocabularies } from "../../action/AsyncActions";
import { useI18n } from "../hook/useI18n";
import Term, { TermData } from "../../model/Term";
import { getLocalized } from "../../model/MultilingualString";
import { getShortLocale } from "../../util/IntlUtil";

interface ImportedVocabulariesListEditProps extends HasI18n {
interface ImportedVocabulariesListEditProps {
vocabulary: Vocabulary;
vocabularies: { [key: string]: Vocabulary };
importedVocabularies?: AssetData[];
onChange: (change: object) => void;
loadVocabularies: () => void;
}

export class ImportedVocabulariesListEdit extends React.Component<ImportedVocabulariesListEditProps> {
public componentDidMount() {
if (Object.getOwnPropertyNames(this.props.vocabularies).length === 0) {
this.props.loadVocabularies();
}
}
const ImportedVocabulariesListEdit: React.FC<ImportedVocabulariesListEditProps> =
({ vocabulary, importedVocabularies, onChange }) => {
const { i18n, locale } = useI18n();
const vocabularies = useSelector(
(state: TermItState) => state.vocabularies
);
const dispatch: ThunkDispatch = useDispatch();
React.useEffect(() => {
if (Object.getOwnPropertyNames(vocabularies).length === 0) {
dispatch(loadVocabularies());
}
}, [dispatch, vocabularies]);

public onChange = (selected: Vocabulary[]) => {
const selectedVocabs = selected.map((v) => ({ iri: v.iri }));
this.props.onChange({ importedVocabularies: selectedVocabs });
};
const onSelect = (selected: Vocabulary[]) => {
const selectedVocabs = selected.map((v) => ({ iri: v.iri }));
onChange({ importedVocabularies: selectedVocabs });
};

public render() {
const i18n = this.props.i18n;
const options = Object.keys(this.props.vocabularies)
.map((v) => this.props.vocabularies[v])
.filter((v) => v.iri !== this.props.vocabulary.iri);
const selected = Utils.sanitizeArray(this.props.importedVocabularies).map(
const options = Object.keys(vocabularies)
.map((v) => vocabularies[v])
.filter((v) => v.iri !== vocabulary.iri);
const selected = Utils.sanitizeArray(importedVocabularies).map(
(v) => v.iri!
);
return (
Expand All @@ -50,11 +54,13 @@ export class ImportedVocabulariesListEdit extends React.Component<ImportedVocabu
</Label>
<IntelligentTreeSelect
className="p-0"
onChange={this.onChange}
onChange={onSelect}
value={selected}
options={options}
valueKey="iri"
labelKey="label"
getOptionLabel={(option: Term | TermData) =>
getLocalized(option.label, getShortLocale(locale))
}
childrenKey="children"
placeholder={i18n("select.placeholder")}
classNamePrefix="react-select"
Expand All @@ -70,14 +76,6 @@ export class ImportedVocabulariesListEdit extends React.Component<ImportedVocabu
</Col>
</Row>
);
}
}
};

export default connect(
(state: TermItState) => ({ vocabularies: state.vocabularies }),
(dispatch: ThunkDispatch) => {
return {
loadVocabularies: () => dispatch(loadVocabularies()),
};
}
)(injectIntl(withI18n(ImportedVocabulariesListEdit)));
export default ImportedVocabulariesListEdit;
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,63 @@ import Vocabulary from "../../../model/Vocabulary";
import Generator from "../../../__tests__/environment/Generator";
import VocabularyUtils from "../../../util/VocabularyUtils";
import { shallow } from "enzyme";
import { ImportedVocabulariesListEdit } from "../ImportedVocabulariesListEdit";
import { intlFunctions } from "../../../__tests__/environment/IntlUtil";
import ImportedVocabulariesListEdit from "../ImportedVocabulariesListEdit";
import { mockUseI18n } from "../../../__tests__/environment/IntlUtil";
// @ts-ignore
import { IntelligentTreeSelect } from "intelligent-tree-select";
import { langString } from "../../../model/MultilingualString";
import * as Redux from "react-redux";
import { ThunkDispatch } from "src/util/Types";
import * as AsyncActions from "../../../action/AsyncActions";
import { loadVocabularies } from "../../../action/AsyncActions";
import { mountWithIntl } from "../../../__tests__/environment/Environment";

jest.mock("react-redux", () => ({
...jest.requireActual("react-redux"),
useSelector: jest.fn(),
useDispatch: jest.fn(),
}));

describe("ImportedVocabulariesListEdit", () => {
let vocabularies: { [key: string]: Vocabulary };
let vocabulary: Vocabulary;

let onChange: (change: object) => void;
let loadVocabularies: () => void;

let fakeDispatch: ThunkDispatch;

beforeEach(() => {
const vOne = new Vocabulary({
iri: Generator.generateUri(),
label: "Vocabulary One",
label: langString("Vocabulary One"),
types: [VocabularyUtils.VOCABULARY],
});
const vTwo = new Vocabulary({
iri: Generator.generateUri(),
label: "Vocabulary two",
label: langString("Vocabulary two"),
types: [VocabularyUtils.VOCABULARY],
});
vocabularies = {};
vocabularies[vOne.iri] = vOne;
vocabularies[vTwo.iri] = vTwo;
vocabulary = new Vocabulary({
iri: Generator.generateUri(),
label: "Edited vocabulary",
label: langString("Edited vocabulary"),
});
vocabularies[vocabulary.iri] = vocabulary;
onChange = jest.fn();
loadVocabularies = jest.fn();
fakeDispatch = jest.fn();
jest.spyOn(Redux, "useDispatch").mockReturnValue(fakeDispatch);
jest.spyOn(AsyncActions, "loadVocabularies");
mockUseI18n();
});

it("loads vocabularies after mount when they are not loaded yet", () => {
shallow(
jest.spyOn(Redux, "useSelector").mockReturnValue({});
mountWithIntl(
<ImportedVocabulariesListEdit
vocabulary={vocabulary}
vocabularies={{}}
loadVocabularies={loadVocabularies}
onChange={onChange}
{...intlFunctions()}
/>
);
expect(loadVocabularies).toHaveBeenCalled();
Expand All @@ -54,77 +68,49 @@ describe("ImportedVocabulariesListEdit", () => {
const wrapper = shallow(
<ImportedVocabulariesListEdit
vocabulary={vocabulary}
vocabularies={vocabularies}
loadVocabularies={loadVocabularies}
onChange={onChange}
{...intlFunctions()}
/>
);
expect(wrapper.find(IntelligentTreeSelect).prop("value")).toEqual([]);
});

it("calls onChange with selected vocabularies IRIs on vocabulary selection", () => {
jest.spyOn(Redux, "useSelector").mockReturnValue(vocabularies);
const vocabularyArray = Object.keys(vocabularies).map(
(v) => vocabularies[v]
);
const wrapper = shallow<ImportedVocabulariesListEdit>(
const wrapper = shallow(
<ImportedVocabulariesListEdit
vocabulary={vocabulary}
loadVocabularies={loadVocabularies}
vocabularies={vocabularies}
onChange={onChange}
{...intlFunctions()}
/>
);
wrapper.instance().onChange(vocabularyArray);
(wrapper.find(IntelligentTreeSelect).prop("onChange") as any)(
vocabularyArray
);
expect(onChange).toHaveBeenCalledWith({
importedVocabularies: vocabularyArray.map((v) => ({ iri: v.iri })),
});
});

it("calls onChange with empty array when vocabulary selector is reset", () => {
const selected = Object.keys(vocabularies).map((k) => ({ iri: k }));
const wrapper = shallow<ImportedVocabulariesListEdit>(
const wrapper = shallow(
<ImportedVocabulariesListEdit
vocabulary={vocabulary}
loadVocabularies={loadVocabularies}
vocabularies={vocabularies}
importedVocabularies={selected}
onChange={onChange}
{...intlFunctions()}
/>
);
wrapper.instance().onChange([]);
(wrapper.find(IntelligentTreeSelect).prop("onChange") as any)([]);
expect(onChange).toHaveBeenCalledWith({ importedVocabularies: [] });
});

it("updates vocabulary selection when props are updated", () => {
const wrapper = shallow<ImportedVocabulariesListEdit>(
<ImportedVocabulariesListEdit
vocabulary={vocabulary}
loadVocabularies={loadVocabularies}
vocabularies={vocabularies}
onChange={onChange}
{...intlFunctions()}
/>
);
expect(wrapper.find(IntelligentTreeSelect).prop("value")).toEqual([]);
const newSelected = [{ iri: Object.keys(vocabularies)[0] }];
wrapper.setProps({ importedVocabularies: newSelected });
wrapper.update();
expect(wrapper.find(IntelligentTreeSelect).prop("value")).toEqual([
newSelected[0].iri,
]);
});

it("does not offer the vocabulary itself for importing", () => {
const wrapper = shallow(
<ImportedVocabulariesListEdit
vocabulary={vocabulary}
vocabularies={vocabularies}
loadVocabularies={loadVocabularies}
onChange={onChange}
{...intlFunctions()}
/>
);
const options: Vocabulary[] = wrapper
Expand Down

0 comments on commit e871a48

Please sign in to comment.