Skip to content
Snippets Groups Projects
Commit cf033923 authored by Esko Ikkala's avatar Esko Ikkala
Browse files

Handle multiple language tags


Co-authored-by: default avatarErkki Heino <erkki.heino@aalto.fi>
parent 30956240
No related branches found
No related tags found
No related merge requests found
import { getLangValue, updateSuggestions } from '../reducers/suggestions';
describe('suggestions', () => {
describe('updateSuggestions', () => {
test('Adds preferred labels', () => {
const results = [
{
'dataset': 'Karelian map names',
'results': []
},
{
'dataset': 'Finnish municipalities',
'results': [{
'uri': 'http://ldf.fi/warsa/places/municipalities/m_place_124',
'label': [
{
'type': 'literal',
'xml:lang': 'fi',
'value': 'Kivijärvi'
}
],
'typeLabel': [
{
'type': 'literal',
'xml:lang': 'fi',
'value': 'Kunta'
},
{
'type': 'literal',
'xml:lang': 'en',
'value': 'Municipality'
}
],
'broaderAreaLabel': [
{
'type': 'literal',
'xml:lang': 'fi',
'value': 'Vaasan lääni'
}
]
}]
}
];
const expected = [
{
'dataset': 'Karelian map names',
'results': []
},
{
'dataset': 'Finnish municipalities',
'results': [{
...results[1].results[0],
'preferredLabel': {
'type': 'literal',
'xml:lang': 'fi',
'value': 'Kivijärvi'
},
'preferredTypeLabel': {
'type': 'literal',
'xml:lang': 'fi',
'value': 'Kunta'
},
'preferredBroaderAreaLabel':
{
'type': 'literal',
'xml:lang': 'fi',
'value': 'Vaasan lääni'
}
}]
}
];
expect(updateSuggestions({ language: 'fi', results })).toEqual(expected);
});
});
describe('getLangValue', () => {
let valueList;
beforeEach(() => {
valueList = [
{
'type': 'literal',
'xml:lang': 'fi',
'value': 'Kunta'
},
{
'type': 'literal',
'xml:lang': 'en',
'value': 'Municipality'
},
{
'type': 'literal',
'value': 'Province'
}
];
});
test('Chooses an object based on language tag', () => {
const expectedEn = valueList[1];
const expectedFi = valueList[0];
expect(getLangValue('en', valueList)).toEqual(expectedEn);
expect(getLangValue('fi', valueList)).toEqual(expectedFi);
});
test('Chooses the first object if the language tag is not found', () => {
const expected = valueList[0];
expect(getLangValue('sv', valueList)).toEqual(expected);
});
});
});
......@@ -9,6 +9,7 @@ export const FETCH_RESULTS = 'FETCH_RESULTS';
export const UPDATE_RESULTS = 'UPDATE_RESULTS';
export const CLEAR_RESULTS = 'CLEAR_RESULTS';
export const CLEAR_ERROR = 'CLEAR_ERROR';
export const UPDATE_LANGUAGE = 'UPDATE_LANGUAGE';
export const updateQuery = (query) => ({
type: UPDATE_QUERY,
......@@ -33,9 +34,9 @@ export const fetchSuggestionsFailed = (error) => ({
error
});
export const updateSuggestions = (results) => ({
export const updateSuggestions = ({ results, language }) => ({
type: UPDATE_SUGGESTIONS,
results
results, language
});
export const clearSuggestions = () => ({
......@@ -58,3 +59,8 @@ export const clearResults = () => ({
export const clearError = () => ({
type: CLEAR_ERROR,
});
export const updateLanguage = (language) => ({
type: UPDATE_LANGUAGE,
language
});
import React from 'react';
import PropTypes from 'prop-types';
import Autosuggest from 'react-autosuggest';
import match from 'autosuggest-highlight/match';
import parse from 'autosuggest-highlight/parse';
import TextField from 'material-ui/TextField';
import Paper from 'material-ui/Paper';
import { MenuItem } from 'material-ui/Menu';
import { withStyles } from 'material-ui/styles';
import SuggestionItem from './SuggestionItem';
function renderInput(inputProps) {
const { classes, ref, ...other } = inputProps;
......@@ -26,26 +24,7 @@ function renderInput(inputProps) {
}
function renderSuggestion(suggestion, { query, isHighlighted }) {
const matches = match(suggestion.label, query);
const parts = parse(suggestion.label, matches);
return (
<MenuItem selected={isHighlighted} component="div">
<div>
{parts.map((part, index) => {
return part.highlight ? (
<strong key={String(index)} style={{ fontWeight: 500 }}>
{part.text}
</strong>
) : (
<span key={String(index)} style={{ fontWeight: 300 }}>
{part.text}
</span>
);
})}
</div>
</MenuItem>
);
return <SuggestionItem suggestion={suggestion} query={query} isHighlighted={isHighlighted} />;
}
function renderSuggestionsContainer(options) {
......@@ -69,7 +48,7 @@ function getSectionSuggestions(section) {
}
function getSuggestionValue(suggestion) {
return suggestion.label;
return suggestion.preferredLabel.value;
}
const styles = theme => ({
......@@ -108,7 +87,7 @@ const IntegrationAutosuggest = (props) => {
const { classes } = props;
// console.log('IntegrationAutosuggest', props);
//console.log('IntegrationAutosuggest', props);
return (
<Autosuggest
......
import React from 'react';
import PropTypes from 'prop-types';
import match from 'autosuggest-highlight/match';
import parse from 'autosuggest-highlight/parse';
import { MenuItem } from 'material-ui/Menu';
const SuggestionItem = ({ suggestion, query, isHighlighted }) => {
const matches = match(suggestion.preferredLabel.value, query);
const parts = parse(suggestion.preferredLabel.value, matches);
return (
<MenuItem selected={isHighlighted} component="div">
<div>
{parts.map((part, index) => {
return part.highlight ? (
<strong key={String(index)} style={{ fontWeight: 500 }}>
{part.text}
</strong>
) : (
<span key={String(index)} style={{ fontWeight: 300 }}>
{part.text}
</span>
);
})}
<sup>{suggestion.preferredLabel['xml:lang']}</sup>
<span>{' ('}
<span>{suggestion.preferredTypeLabel.value}</span>
<span>{', '}{suggestion.preferredBroaderAreaLabel.value}</span>
{')'}
</span>
</div>
</MenuItem>
);
};
SuggestionItem.propTypes = {
suggestion: PropTypes.object.isRequired,
query: PropTypes.string.isRequired,
isHighlighted: PropTypes.bool.isRequired,
};
export default SuggestionItem;
......@@ -12,6 +12,7 @@ const getSuggestionsEpic = (action$, store) => {
.debounceTime(500)
.switchMap(() => {
const { query, datasets } = store.getState().search;
const language = store.getState().options.language;
if (query.length < 3) {
return [];
}
......@@ -19,7 +20,7 @@ const getSuggestionsEpic = (action$, store) => {
const requestUrl = `${searchUrl}?q=${query}&${dsParams}`;
return ajax.getJSON(requestUrl)
.map(response => updateSuggestions(response))
.map(response => updateSuggestions({ results: response, language }))
.catch(error => Observable.of({
type: FETCH_SUGGESTIONS_FAILED,
error: error,
......
......@@ -8,8 +8,7 @@ import rootEpic from './epics';
import ReduxToastr from 'react-redux-toastr';
import 'react-redux-toastr/lib/css/react-redux-toastr.min.css';
import { bindActionCreators } from 'redux';
import {actions as toastrActions} from 'react-redux-toastr';
import { actions as toastrActions } from 'react-redux-toastr';
import App from './components/App';
const store = createStore(
......
export const logger = store => next => action => {
console.log('dispatching', action);
let result = next(action);
console.log('next state', store.getState());
return result;
};
export const crashReporter = store => next => action => {
try {
return next(action);
} catch (err) {
console.error('Caught an exception!', err);
console.log('State', store.getState());
throw err;
}
};
......@@ -3,8 +3,10 @@ import { combineReducers } from 'redux';
import {reducer as toastrReducer} from 'react-redux-toastr';
import search from './search';
import error from './error';
import options from './options';
const reducer = combineReducers({
options,
search,
error,
toastr: toastrReducer,
......
import { UPDATE_LANGUAGE } from '../actions';
const DEFAULT_LANGUAGE = 'en';
export const INITIAL_STATE = {
language: DEFAULT_LANGUAGE
};
const options = (state = INITIAL_STATE, action) => {
switch (action.type) {
case UPDATE_LANGUAGE:
return { ...state, language: action.language || DEFAULT_LANGUAGE };
default:
return state;
}
};
export default options;
import {
UPDATE_QUERY,
FETCH_SUGGESTIONS,
UPDATE_DATASETS,
UPDATE_SUGGESTIONS,
CLEAR_SUGGESTIONS,
} from '../actions';
import suggestions from './suggestions';
export const INITIAL_STATE = {
query: '',
......@@ -16,17 +16,11 @@ const search = (state = INITIAL_STATE, action) => {
switch (action.type) {
case UPDATE_QUERY:
return { ...state, query: action.query || '' };
case FETCH_SUGGESTIONS:
return { ...state };
case UPDATE_DATASETS:
return { ...state, datasets: action.datasets || [] };
case UPDATE_SUGGESTIONS:
return {
...state,
suggestions: action.results || [],
};
case CLEAR_SUGGESTIONS:
return { ...state, suggestions: [] };
case UPDATE_SUGGESTIONS:
return { ...state, suggestions: suggestions(state.suggestions, action) };
default:
return state;
}
......
import _ from 'lodash';
import {
UPDATE_SUGGESTIONS,
CLEAR_SUGGESTIONS,
} from '../actions';
export const getLangValue = (language, valueList) =>
_.find(valueList, ['xml:lang', language]) || _.first(valueList) || {};
export const updateDatasetSuggestions = ({ language, results }) => {
return _.map(results, (suggestion) => ({
...suggestion,
preferredLabel: getLangValue(language, suggestion.label),
preferredTypeLabel: getLangValue(language, suggestion.typeLabel),
preferredBroaderAreaLabel: getLangValue(language, suggestion.broaderAreaLabel),
}));
};
export const updateSuggestions = ({ language, results }) => {
return _.map(results, (result) => ({
...result,
results: updateDatasetSuggestions({ language, results: result.results })
}));
};
const suggestions = (state = [], action) => {
switch (action.type) {
case UPDATE_SUGGESTIONS:
return updateSuggestions(action);
case CLEAR_SUGGESTIONS:
return [];
default:
return state;
}
};
export default suggestions;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment