Skip to content

Commit

Permalink
Application language support (#83)
Browse files Browse the repository at this point in the history
* Tatt i bruk det nye endepunktet og startet på utvikling av meny for valg av spraak.

* Legg til språk dropdown

* Oppdaterte appLanguages til et dictionary.

* Add language select with label

* Fix tests after adding language combobox

* Language combobx rename variables

* Endret på visning av backArrow og closeButton

* Rebase language branch fixes

* Show error when app language has errors

* Update snapshot in test

* Fix from review

* Renamed the schema-description from showLanguageDropdown to showLanguageSelector

* Applied the requested changes

* Added the missing attributes and removed the unused ones

* REU-466; Fiks after review

* REU-466; Fix after rebase

* Application language fix back button test and run app language saga when the latest layoutsettings is fullfilled

* App language remove error when fetch fails. Will only remove app language Select instead

* Tatt i bruk det nye endepunktet og startet på utvikling av meny for valg av spraak.

* Legg til språk dropdown

* Oppdaterte appLanguages til et dictionary.

* Add language select with label

* Fix tests after adding language combobox

* Language combobx rename variables

* Endret på visning av backArrow og closeButton

* Rebase language branch fixes

* Show error when app language has errors

* Update snapshot in test

* Fix from review

* Renamed the schema-description from showLanguageDropdown to showLanguageSelector

* Applied the requested changes

* Added the missing attributes and removed the unused ones

* REU-466; Fiks after review

* REU-466; Fix after rebase

* Application language fix back button test and run app language saga when the latest layoutsettings is fullfilled

* App language remove error when fetch fails. Will only remove app language Select instead

* Added the workaround for rendering missing variables in text resources when chaning language

* Reverted the workaround

* AppLanguage: Update to redux toolkit

* Language selector: Combine selector with allowAnonymous and revert unchanged files

* App language: Fiks and write tests

* App language: Fix after review

* App language; merge from master and add test for navigation

Co-authored-by: Truls Gunnerød <[email protected]>
Co-authored-by: laklov <[email protected]>
Co-authored-by: Lasse Klovstad <[email protected]>
  • Loading branch information
4 people authored Jun 17, 2022
1 parent c53a978 commit 2db4ae2
Show file tree
Hide file tree
Showing 36 changed files with 765 additions and 356 deletions.
17 changes: 2 additions & 15 deletions src/altinn-app-frontend/__mocks__/formLayoutStateMock.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ILayoutState } from '../src/features/form/layout/formLayoutSlice';
import { getUiConfigStateMock } from './uiConfigStateMock';

export function getFormLayoutStateMock(customStates?: Partial<ILayoutState>): ILayoutState {
const mockFormLayoutState: ILayoutState = {
Expand Down Expand Up @@ -46,21 +47,7 @@ export function getFormLayoutStateMock(customStates?: Partial<ILayoutState>): IL
],
},
error: null,
uiConfig: {
autoSave: true,
focus: null,
hiddenFields: [],
repeatingGroups: {
group: {
index: 1,
dataModelBinding: 'someGroup',
}
},
fileUploadersWithTag: null,
currentView: 'FormLayout',
navigationConfig: {},
layoutOrder: ['FormLayout'],
},
uiConfig: getUiConfigStateMock(),
layoutsets: null,
};

Expand Down
26 changes: 17 additions & 9 deletions src/altinn-app-frontend/__mocks__/initialStateMock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ export const mockParty: IParty = {
childParties: null,
};

export function getInitialStateMock(customStates?: Partial<IRuntimeState>): IRuntimeState {
export function getInitialStateMock(
customStates?: Partial<IRuntimeState>,
): IRuntimeState {
const initialState: IRuntimeState = {
applicationMetadata: {
applicationMetadata: applicationMetadataMock,
Expand Down Expand Up @@ -65,6 +67,7 @@ export function getInitialStateMock(customStates?: Partial<IRuntimeState>): IRun
},
language: {
language: getLanguageFromCode('nb'),
selectedAppLanguage: '',
error: null,
},
organisationMetaData: {
Expand All @@ -78,19 +81,14 @@ export function getInitialStateMock(customStates?: Partial<IRuntimeState>): IRun
logo: '',
orgnr: '',
homepage: '',
environments: [
'tt02',
'production',
],
environments: ['tt02', 'production'],
},
},
error: null,
},
party: {
error: null,
parties: [
mockParty,
],
parties: [mockParty],
selectedParty: mockParty,
},
process: {
Expand All @@ -108,7 +106,17 @@ export function getInitialStateMock(customStates?: Partial<IRuntimeState>): IRun
},
textResources: {
resources: [
{ id: 'option.from.rep.group.label', value: 'The value from the group is: {0}', unparsedValue: 'The value from the group is: {0}', variables: [ { dataSource: 'dataModel.skjema', key: 'someGroup[{0}].labelField' }] }
{
id: 'option.from.rep.group.label',
value: 'The value from the group is: {0}',
unparsedValue: 'The value from the group is: {0}',
variables: [
{
dataSource: 'dataModel.skjema',
key: 'someGroup[{0}].labelField',
},
],
},
],
error: null,
language: 'nb',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { IApplicationMetadata } from 'src/shared/resources/applicationMetadata';
import { IRuntimeState } from 'src/types';
import { getInitialStateMock } from './initialStateMock';

export const statelessAndAllowAnonymousMock = (allowAnonymous: boolean | undefined) => {
const initialState = getInitialStateMock();
const initialAppMetadata: IApplicationMetadata = {
...initialState.applicationMetadata.applicationMetadata,
onEntry: {
show: 'stateless',
},
};
initialAppMetadata.dataTypes[0].appLogic.allowAnonymousOnStateless = allowAnonymous;
const mockInitialState: IRuntimeState = {
...initialState,
applicationMetadata: {
applicationMetadata: initialAppMetadata,
error: null,
},
formLayout: {
...initialState.formLayout,
layoutsets: {
sets: [
{
id: 'stateless',
dataType: 'test-data-model',
},
],
},
},
};
return mockInitialState;
};
20 changes: 20 additions & 0 deletions src/altinn-app-frontend/__mocks__/uiConfigStateMock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { IUiConfig } from 'src/types';

export const getUiConfigStateMock = (customStates?: Partial<IUiConfig>): IUiConfig=>{
return {
autoSave: true,
focus: null,
hiddenFields: [],
repeatingGroups: {
group: {
index: 1,
dataModelBinding: 'someGroup',
}
},
fileUploadersWithTag: null,
currentView: 'FormLayout',
navigationConfig: {},
layoutOrder: ['FormLayout'],
...customStates
}
}
49 changes: 13 additions & 36 deletions src/altinn-app-frontend/src/components/base/DropdownComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
import React from 'react';
import cn from 'classnames';
import { makeStyles } from '@material-ui/core';

import { AltinnAppTheme } from 'altinn-shared/theme';
import { useAppSelector, useHasChangedIgnoreUndefined } from 'src/common/hooks';
import { IComponentProps } from '..';

import '../../styles/shared.css';
import type { IMapping, IOptionSource } from 'src/types';
import { getOptionLookupKey } from 'src/utils/options';
import { AltinnSpinner } from 'altinn-shared/components';
import { AltinnSpinner, Select } from 'altinn-shared/components';
import { useGetOptions } from '../hooks';

export interface IDropdownProps extends IComponentProps {
Expand All @@ -19,19 +13,6 @@ export interface IDropdownProps extends IComponentProps {
source?: IOptionSource;
}

const useStyles = makeStyles({
select: {
fontSize: '1.6rem',
'&:focus': {
outline: `2px solid ${AltinnAppTheme.altinnPalette.primary.blueDark}`,
},
},
});

const optionStyle = {
display: 'none',
};

function DropdownComponent({
optionsId,
formData,
Expand All @@ -44,7 +25,6 @@ function DropdownComponent({
mapping,
source,
}: IDropdownProps) {
const classes = useStyles();
const options = useGetOptions({ optionsId, mapping, source });
const fetchingOptions = useAppSelector(
(state) =>
Expand Down Expand Up @@ -89,26 +69,23 @@ function DropdownComponent({
{fetchingOptions ? (
<AltinnSpinner />
) : (
<select
<Select
id={id}
value={formData?.simpleBinding}
disabled={readOnly}
className={cn(classes.select, 'custom-select a-custom-select', {
'validation-error': !isValid,
'disabled !important': readOnly,
})}
onChange={handleChange}
onBlur={handleBlur}
>
<option style={optionStyle} />
{options?.map((option, index) => (
<option key={index} value={option.value}>
{getTextResourceAsString(option.label)}
</option>
))}
</select>
value={formData?.simpleBinding}
disabled={readOnly}
error={!isValid}
options={
options?.map((option) => ({
label: getTextResourceAsString(option.label),
value: option.value,
})) || []
}
/>
)}
</>
);
}

export default DropdownComponent;
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { getLanguageFromKey } from 'altinn-shared/utils';
import * as React from 'react';
import { useAppSelector } from 'src/common/hooks';

interface CloseButtonProps {
handleClose: () => void;
};

export const CloseButton = ({ handleClose }: CloseButtonProps) => {
const language = useAppSelector(state => state.language.language || {});
return <button
type='button'
className='a-modal-close a-js-tabable-popover ml-1'
aria-label={getLanguageFromKey('general.close_schema', language)}
onClick={handleClose}
>
<span className='ai-stack'>
<i
className='ai-stack-1x ai ai-exit a-modal-close-icon'
aria-hidden='true'
/>
</span>
<span className='sr-only'>
{getLanguageFromKey('general.close_schema', language)}
</span>
</button>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { getTextFromAppOrDefault } from 'src/utils/textResource';
import { AltinnSpinner, Select } from 'altinn-shared/components';
import { Box } from '@material-ui/core';
import * as React from 'react';
import { useAppSelector } from 'src/common/hooks';
import { useGetAppLanguageQuery } from 'src/services/LanguageApi';
import { LanguageActions } from 'src/shared/resources/language/languageSlice';
import { useDispatch } from 'react-redux';
import { appLanguageStateSelector } from 'src/selectors/appLanguageStateSelector';

export const LanguageSelector = () => {
const language = useAppSelector(state => state.language.language || {});
const { isSuccess, data, isLoading } = useGetAppLanguageQuery();
const selectedAppLanguage = useAppSelector(
appLanguageStateSelector,
);

const textResources = useAppSelector(state => state.textResources.resources);
const dispatch = useDispatch();
const handleAppLanguageChange = (languageCode: string) => {
dispatch(LanguageActions.updateSelectedAppLanguage({ selected: languageCode }));
};


return <Box display='flex' flexDirection='column' className='mb-1'>
{isLoading && <AltinnSpinner />}
{isSuccess && <>
<label className='a-form-label' htmlFor='app-language-select'>
{getTextFromAppOrDefault(
'language.selector.label',
textResources,
language,
null,
true,
)}
</label>
<Select
options={data.map((l) => ({
value: l.language,
label: getTextFromAppOrDefault(
'language.full_name.' + l.language,
textResources,
language,
null,
true,
),
}))}
onChange={(ev) => handleAppLanguageChange(ev.target.value)}
value={selectedAppLanguage}
id='app-language-select'
/>
</>}
</Box>;
};
Loading

0 comments on commit 2db4ae2

Please sign in to comment.