diff --git a/sass/widgets/common/WidgetForm.scss b/sass/widgets/common/WidgetForm.scss
index f8030481d..60dc88b85 100644
--- a/sass/widgets/common/WidgetForm.scss
+++ b/sass/widgets/common/WidgetForm.scss
@@ -2,7 +2,6 @@
.WidgetForm {
&__body {
display: flex;
- height: 100%;
}
&__info {
diff --git a/sass/widgets/common/WidgetPage.scss b/sass/widgets/common/WidgetPage.scss
index e91b4b906..2e8ff1f2a 100644
--- a/sass/widgets/common/WidgetPage.scss
+++ b/sass/widgets/common/WidgetPage.scss
@@ -1,7 +1,6 @@
// sass-lint:disable class-name-format no-important
.WidgetPage {
- height: 100%;
- min-height: 700px;
+ min-height: 100%;
margin-bottom: 0;
background-color: $color-grey-background;
diff --git a/src/ui/common/formik-field/FilterValueOptionSelector.js b/src/ui/common/formik-field/FilterValueOptionSelector.js
new file mode 100644
index 000000000..fa6033bf2
--- /dev/null
+++ b/src/ui/common/formik-field/FilterValueOptionSelector.js
@@ -0,0 +1,653 @@
+import React, { Fragment, Component } from 'react';
+import PropTypes from 'prop-types';
+import { get, isUndefined, isEqual } from 'lodash';
+import { FormattedMessage, FormattedHTMLMessage, intlShape } from 'react-intl';
+import { Collapse } from 'react-collapse';
+import { Field } from 'formik';
+import FormLabel from 'ui/common/form/FormLabel';
+import RadioInput from 'ui/common/formik-field/RenderRadioInput';
+import TextInput from 'ui/common/formik-field/RenderTextInput';
+import DateFilterInput from 'ui/common/form/RenderDateFilterInput';
+import SwitchRenderer from 'ui/common/formik-field/RenderSwitchInput';
+
+import {
+ TEXT_FILTERABLE_ATTRIBUTES,
+ DATE_FILTERABLE_ATTRIBUTES,
+ BOOL_FILTERABLE_ATTRIBUTES,
+} from 'state/content-type/selectors';
+
+const TEXT_FILTERABLE = 'text_filterable_type';
+const DATE_FILTERABLE = 'date_filterable_type';
+const BOOL_FILTERABLE = 'boolean_filterable_type';
+
+const HAS_VALUE = 'valuePresence';
+const HAS_NO_VALUE = 'valueAbsence';
+const BY_VALUE_ONLY = 'valueOnly';
+const BY_VALUE_PARTIAL = 'valuePartial';
+const BY_RANGE = 'valueRange';
+
+const CLEAN_VALUES = {
+ value: undefined,
+ nullValue: undefined,
+ likeOption: undefined,
+ valueDateDelay: undefined,
+ startDateDelay: undefined,
+ endDateDelay: undefined,
+ start: undefined,
+ end: undefined,
+};
+
+class FilterValueOptionSelector extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ filterableType: '',
+ expanded: false,
+ optionSelected: '',
+ keyChanged: false,
+ };
+ this.handleHeadlineClick = this.handleHeadlineClick.bind(this);
+ this.handleValueChange = this.handleValueChange.bind(this);
+ this.handleValueTypeChange = this.handleValueTypeChange.bind(this);
+ }
+
+ componentDidMount() {
+ this.beginFillState();
+ }
+
+ componentDidUpdate(prevProps) {
+ const {
+ attributeFilterChoices: prevAttr,
+ value: prevValue,
+ } = prevProps;
+ const { key: prevKey } = prevValue;
+
+ const {
+ attributeFilterChoices: attr,
+ value,
+ } = this.props;
+ const { key } = value;
+
+ const hasAllValueChanged = !isEqual({ ...prevValue, key: 1 }, { ...value, key: 1 });
+
+ const keyChanged = prevKey !== key;
+ if (keyChanged || prevAttr.length !== attr.length) {
+ // eslint-disable-next-line react/no-did-update-set-state
+ this.setState({ keyChanged });
+ this.beginFillState(hasAllValueChanged);
+ }
+ }
+
+ componentWillUnmount() {
+ this.setState({
+ filterableType: '',
+ expanded: false,
+ optionSelected: '',
+ });
+ }
+
+ getAttributeType() {
+ const { value } = this.props;
+ const { key } = value;
+ const { attributeFilterChoices } = this.props;
+ const selectedAttributeType = attributeFilterChoices.find(attribute => attribute.code === key);
+ return get(selectedAttributeType, 'type', '');
+ }
+
+ beginFillState(valuePropChanged) {
+ const {
+ attributeFilterChoices: attrChoices,
+ value: { attributeFilter },
+ } = this.props;
+ const { keyChanged } = this.state;
+
+ if (attributeFilter && attrChoices && attrChoices.length > 0) {
+ const filterableType = this.determineFilterableType();
+ if (keyChanged && !valuePropChanged) {
+ if (filterableType === BOOL_FILTERABLE) {
+ this.handleValueChange({ ...CLEAN_VALUES, value: true });
+ } else {
+ this.handleValueChange({ ...CLEAN_VALUES });
+ }
+ }
+ // eslint-disable-next-line react/no-did-update-set-state
+ this.setState({
+ filterableType,
+ optionSelected: this.determineOptionSelected(filterableType),
+ });
+ }
+ }
+
+ determineFilterableType() {
+ const type = this.getAttributeType();
+ if (TEXT_FILTERABLE_ATTRIBUTES.includes(type)) {
+ return TEXT_FILTERABLE;
+ }
+ if (DATE_FILTERABLE_ATTRIBUTES.includes(type)) {
+ return DATE_FILTERABLE;
+ }
+ if (BOOL_FILTERABLE_ATTRIBUTES.includes(type)) {
+ return BOOL_FILTERABLE;
+ }
+ return '';
+ }
+
+ determineOptionSelected(filterableType) {
+ const { value: propValue } = this.props;
+ const {
+ value,
+ start,
+ end,
+ nullValue,
+ } = propValue;
+ if (!isUndefined(start) || !isUndefined(end)) {
+ return BY_RANGE;
+ }
+ if (value) {
+ return filterableType === TEXT_FILTERABLE ? BY_VALUE_PARTIAL : BY_VALUE_ONLY;
+ }
+ if (nullValue) {
+ return HAS_NO_VALUE;
+ }
+ return HAS_VALUE;
+ }
+
+ handleHeadlineClick() {
+ const { expanded } = this.state;
+ this.setState({ expanded: !expanded });
+ }
+
+ handleValueChange(newProp) {
+ const { value: propValue, onChange, fieldIndex } = this.props;
+ const newValue = {
+ ...propValue,
+ ...newProp,
+ };
+ onChange(newValue, fieldIndex);
+ }
+
+ resetValueKeys(option, filterableType) {
+ switch (option) {
+ case BY_VALUE_ONLY:
+ if (filterableType === DATE_FILTERABLE) {
+ this.handleValueChange({ ...CLEAN_VALUES, value: 'today' });
+ } else {
+ this.handleValueChange({ ...CLEAN_VALUES, value: '' });
+ }
+ break;
+ case BY_VALUE_PARTIAL:
+ this.handleValueChange({ ...CLEAN_VALUES, value: '', likeOption: false });
+ break;
+ case BY_RANGE:
+ this.handleValueChange({ ...CLEAN_VALUES, start: '', end: '' });
+ break;
+ case HAS_NO_VALUE:
+ this.handleValueChange({ ...CLEAN_VALUES, nullValue: true });
+ break;
+ case HAS_VALUE:
+ default:
+ this.handleValueChange({ ...CLEAN_VALUES });
+ }
+ }
+
+ handleValueTypeChange({ currentTarget: { value } }) {
+ const { filterableType } = this.state;
+ this.resetValueKeys(value, filterableType);
+ this.setState({ optionSelected: value });
+ }
+
+ renderLabelWithSort(node) {
+ const { value } = this.props;
+ const { order } = value;
+ return (
+
+
+ |
+
+ |
+
+ |
+
+ |
+
---|