Skip to content

Commit

Permalink
feature/376-dropdown-component (#4593)
Browse files Browse the repository at this point in the history
* Added dropdown-component
  • Loading branch information
lorang92 authored Aug 11, 2020
1 parent a0bbb30 commit 79a4597
Show file tree
Hide file tree
Showing 24 changed files with 379 additions and 183 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ export function getInitialStateMock(customStates?: Partial<IRuntimeState>): IRun
error: null,
language: 'nb',
},
optionState: {
options: {},
error: null,
},
};

return {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,45 +1,57 @@
// /* tslint:disable:jsx-wrap-multiline */
import 'jest';
import * as React from 'react';
import * as renderer from 'react-test-renderer';

import { DropdownComponent } from '../../../src/components/base/DropdownComponent';
import configureStore from 'redux-mock-store';
import { Provider } from 'react-redux';
import { render } from '@testing-library/react';
import DropdownComponent from '../../../src/components/base/DropdownComponent';

describe('>>> components/base/DropdownComponent.tsx --- Snapshot', () => {
let mockId: string;
let mockOptions: any[];
let mockOptionsId;
let mockFormData: any;
let mockInitialState: any;
let mockStore: any;
let mockHandleDataChange: (value: any) => void;
let mockGetTextResource: (resourceKey: string) => string;
let mockIsValid: boolean;

beforeEach(() => {
const createStore = configureStore();
mockId = 'mock-id';
mockOptionsId = 'test';
mockFormData = '';
mockOptions = [{
label: 'test-label-1',
value: 'test-1',
}, {
label: 'test-label-2',
value: 'test-2',
}];
mockHandleDataChange = (data: any) => null;
mockGetTextResource = (resourceKey: string) => 'test';
mockInitialState = {
optionState: {
options: {
test: [
{ value: 'some_value', label: 'some_label' },
{ value: 'some_value_2', label: 'some_label_2' }
],
},
},
};
mockHandleDataChange = () => null;
mockGetTextResource = () => 'test';
mockIsValid = true;
mockStore = createStore(mockInitialState);
});

it('>>> Capture snapshot of DropdownComponent', () => {
const rendered = renderer.create(
<DropdownComponent
id={mockId}
options={mockOptions}
formData={mockFormData}
handleDataChange={mockHandleDataChange}
getTextResource={mockGetTextResource}
isValid={mockIsValid}
/>,
const rendered = render(
<Provider store={mockStore}>
<DropdownComponent
id={mockId}
formData={mockFormData}
handleDataChange={mockHandleDataChange}
getTextResourceAsString={mockGetTextResource}
optionsId={mockOptionsId}
isValid={mockIsValid}
readOnly={false}
/>
</Provider>,
);
expect(rendered).toMatchSnapshot();
expect(rendered.asFragment()).toMatchSnapshot();
});
// it('+++ should trigger onDataChanged on change', () => {
// const mountedDropdownComponent = mount(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`>>> components/base/DropdownComponent.tsx --- Snapshot >>> Capture snapshot of DropdownComponent 1`] = `
<select
className="custom-select a-custom-select"
id="mock-id"
onChange={[Function]}
value=""
>
<option
value="test-1"
<DocumentFragment>
<select
class="custom-select a-custom-select"
id="mock-id"
>
test-label-1
</option>
<option
value="test-2"
>
test-label-2
</option>
</select>
<option
value="some_value"
>
test
</option>
<option
value="some_value_2"
>
test
</option>
</select>
</DocumentFragment>
`;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "altinn-app-frontend",
"version": "2.1.7",
"version": "2.1.8",
"description": "",
"main": "index.js",
"scripts": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import * as React from 'react';
import { useSelector } from 'react-redux';
import { IRuntimeState } from 'src/types';
import '../../styles/shared.css';
import classNames from 'classnames';

export interface IDropdownProps {
formData: any;
getTextResource: (resourceKey: string) => string;
handleDataChange: (value: any) => void;
formData: string;
getTextResourceAsString: (resourceKey: string) => string;
handleDataChange: (value: string) => void;
id: string;
isValid?: boolean;
options: any[];
optionsId: string;
readOnly: boolean;
}

export interface IDropdownState {
Expand All @@ -15,28 +20,30 @@ export interface IDropdownState {
name: string;
}

export class DropdownComponent
extends React.Component<IDropdownProps, IDropdownState> {
function DropdownComponent(props: IDropdownProps) {
const options = useSelector((state: IRuntimeState) => state.optionState.options[props.optionsId]);

public onDataChanged = (e: any) => {
this.props.handleDataChange(e.target.value);
}
const handleOnChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
props.handleDataChange(event.target.value);
};

public render() {
return (
<select
id={this.props.id}
value={this.props.formData}
className={this.props.isValid ? 'custom-select a-custom-select'
: 'custom-select a-custom-select validation-error'}
onChange={this.onDataChanged}
>
{this.props.options.map((option, index) => (
<option key={index} value={option.value}>
{option.label}
</option>
))}
</select>
);
}
return (
<select
id={props.id}
value={props.formData}
disabled={props.readOnly}
className={classNames('custom-select a-custom-select', { 'validation-error': !props.isValid, 'disabled !important': props.readOnly })}
onChange={handleOnChange}
>
{options?.map((option, index) => (
<option
key={index}
value={option.value}
>
{props.getTextResourceAsString(option.label)}
</option>
))}
</select>
);
}
export default DropdownComponent;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import classNames = require('classnames');
import * as React from 'react';
import '../../styles/shared.css';
import classNames = require('classnames');

export interface IInputProps {
id: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/* eslint-disable no-shadow */
import { AddressComponent } from './advanced/AddressComponent';
import { ButtonComponent } from './base/ButtonComponent';
import { CheckboxContainerComponent } from './base/CheckboxesContainerComponent';
// eslint-disable-next-line import/no-cycle
import DatepickerComponent from './base/DatepickerComponent';
import { DropdownComponent } from './base/DropdownComponent';
import DropdownComponent from './base/DropdownComponent';
import { FileUploadComponent } from './base/FileUploadComponent';
import { HeaderComponent } from './base/HeaderComponent';
import { InputComponent } from './base/InputComponent';
Expand Down Expand Up @@ -64,9 +65,9 @@ export const schemaComponents: IComponent[] = [
Type: ComponentTypes.Datepicker,
customProperties: {
readOnly: false,
format: "DD/MM/YYYY",
minDate: "1900-01-01T12:00:00.000Z",
maxDate: "2100-01-01T12:00:00.000Z"
format: 'DD/MM/YYYY',
minDate: '1900-01-01T12:00:00.000Z',
maxDate: '2100-01-01T12:00:00.000Z',
},
},
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {
combineReducers,
/* eslint-disable import/no-cycle */
import { combineReducers,
Reducer,
ReducersMapObject,
} from 'redux';
ReducersMapObject } from 'redux';
import OptionsReducer, { IOptionsState } from '../shared/resources/options/optionsReducer';
import FormDataReducer, { IFormDataState } from '../features/form/data/formDataReducer';
import FormDataModel, { IDataModelState } from '../features/form/datamodel/formDatamodelReducer';
import { IFormDynamicState } from '../features/form/dynamics';
Expand All @@ -20,10 +20,10 @@ import PartyReducer, { IPartyState } from '../shared/resources/party/partyReduce
import processReducer, { IProcessState } from '../shared/resources/process/processReducer';
import ProfileReducer, { IProfileState } from '../shared/resources/profile/profileReducers';
import TextResourcesReducer, { ITextResourcesState } from '../shared/resources/textResources/textResourcesReducer';
import IsLoadingReducer, { IIsLoadingState } from './../shared/resources/isLoading/isLoadingReducers';
import QueueReducer, { IQueueState } from './../shared/resources/queue/queueReducer';
import IsLoadingReducer, { IIsLoadingState } from '../shared/resources/isLoading/isLoadingReducers';
import QueueReducer, { IQueueState } from '../shared/resources/queue/queueReducer';

export interface IReducers<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18> {
export interface IReducers<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19> {
formLayout: T1;
formData: T2;
formDataModel: T3;
Expand All @@ -42,6 +42,7 @@ export interface IReducers<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T1
process: T16;
isLoading: T17;
queue: T18;
optionState: T19
}

export interface IRuntimeReducers extends IReducers<
Expand All @@ -62,7 +63,8 @@ export interface IRuntimeReducers extends IReducers<
Reducer<IInstanceDataState>,
Reducer<IProcessState>,
Reducer<IIsLoadingState>,
Reducer<IQueueState>
Reducer<IQueueState>,
Reducer<IOptionsState>
>,
ReducersMapObject {
}
Expand All @@ -86,6 +88,7 @@ const reducers: IRuntimeReducers = {
profile: ProfileReducer,
queue: QueueReducer,
textResources: TextResourcesReducer,
optionState: OptionsReducer,
};

export default combineReducers(reducers);
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ import PartySagas from '../shared/resources/party/partySagas';
import { processSagas } from '../shared/resources/process/processSagas';
import ProfileSagas from '../shared/resources/profile/profileSagas';
import TextResourcesSagas from '../shared/resources/textResources/textResourcesSagas';
import IsLoadingSagas from './../shared/resources/isLoading/isLoadingSagas';
import QueueSagas from './../shared/resources/queue/queueSagas';
import IsLoadingSagas from '../shared/resources/isLoading/isLoadingSagas';
import QueueSagas from '../shared/resources/queue/queueSagas';
import OptionSagas from '../shared/resources/options/optionsSagas';

function* root(): SagaIterator {
yield fork(FormDataSagas);
Expand All @@ -40,6 +41,7 @@ function* root(): SagaIterator {
yield fork(processSagas);
yield fork(IsLoadingSagas);
yield fork(QueueSagas);
yield fork(OptionSagas);
}

export const initSagas: () => Task = () => sagaMiddleware.run(root);
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IRuntimeState } from "src/types";
import { createSelector } from "reselect";
import { IRuntimeState } from 'src/types';
import { createSelector } from 'reselect';

/**
* Selector for determinig if we have an error in one of our api calls.
Expand All @@ -18,7 +18,8 @@ const getHasErrorsSelector = (state: IRuntimeState) => {
state.formDynamics.error ||
state.instanceData.error ||
state.applicationMetadata.error ||
state.formDataModel.error);
state.formDataModel.error ||
state.optionState.error);

if (error !== null) {
// we have an error on something we consider critical, return true
Expand All @@ -28,27 +29,27 @@ const getHasErrorsSelector = (state: IRuntimeState) => {
// we have a few special cases where we allow 404 status codes but not other errors
const textResourceError = state.textResources.error;
if (textResourceError !== null) {
if (textResourceError.message.indexOf("404") === -1) {
if (textResourceError.message.indexOf('404') === -1) {
hasError = true;
}
}

const formDynamicsError = state.formDynamics.error;
if (formDynamicsError !== null) {
if (formDynamicsError.message.indexOf("404") === -1) {
if (formDynamicsError.message.indexOf('404') === -1) {
hasError = true;
}
}

const formRulesError = state.formRules.error;
if (formRulesError !== null) {
if (formRulesError.message.indexOf("404") === -1) {
if (formRulesError.message.indexOf('404') === -1) {
hasError = true;
}
}

return hasError;
}
};

const getHasErrors = () => {
return createSelector(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const moduleName: string = 'OPTIONS';

export const FETCH_OPTIONS: string = `${moduleName}.FETCH_OPTIONS`;
export const FETCH_OPTIONS_FULFILLED: string = `${moduleName}.FETCH_OPTIONS_FULFILLED`;
export const FETCH_OPTIONS_REJECTED: string = `${moduleName}.FETCH_OPTIONS_REJECTED`;
Loading

0 comments on commit 79a4597

Please sign in to comment.