diff --git a/components/ActionSheet/ActionSheet.js b/components/ActionSheet/ActionSheet.js
index 79532cbf..90ead9f5 100644
--- a/components/ActionSheet/ActionSheet.js
+++ b/components/ActionSheet/ActionSheet.js
@@ -33,14 +33,6 @@ class ActionSheet extends PureComponent {
return null;
}
- static propTypes = {
- style: PropTypes.any,
- confirmOptions: PropTypes.arrayOf(optionPropType),
- cancelOptions: PropTypes.arrayOf(optionPropType),
- active: PropTypes.bool,
- onDismiss: PropTypes.func,
- };
-
constructor(props) {
super(props);
@@ -187,4 +179,19 @@ class ActionSheet extends PureComponent {
}
}
+ActionSheet.propTypes = {
+ style: PropTypes.object.isRequired,
+ active: PropTypes.bool,
+ cancelOptions: PropTypes.arrayOf(optionPropType),
+ confirmOptions: PropTypes.arrayOf(optionPropType),
+ onDismiss: PropTypes.func,
+};
+
+ActionSheet.defaultProps = {
+ active: false,
+ cancelOptions: undefined,
+ confirmOptions: undefined,
+ onDismiss: undefined,
+};
+
export default connectStyle('shoutem.ui.ActionSheet')(ActionSheet);
diff --git a/components/ActionSheet/ActionSheetOption.js b/components/ActionSheet/ActionSheetOption.js
index 1e884aa5..e89a053b 100644
--- a/components/ActionSheet/ActionSheetOption.js
+++ b/components/ActionSheet/ActionSheetOption.js
@@ -26,9 +26,14 @@ function ActionSheetOption({ style, option, cancelOption }) {
}
ActionSheetOption.propTypes = {
- style: PropTypes.any,
- option: optionPropType,
+ style: PropTypes.object.isRequired,
cancelOption: PropTypes.bool,
+ option: optionPropType,
+};
+
+ActionSheetOption.defaultProps = {
+ option: undefined,
+ cancelOption: false,
};
export default connectStyle('shoutem.ui.ActionSheetOption')(ActionSheetOption);
diff --git a/components/Button.js b/components/Button.js
index 662c4168..eabcc9e5 100644
--- a/components/Button.js
+++ b/components/Button.js
@@ -5,18 +5,14 @@ import { connectStyle } from '@shoutem/theme';
class Button extends PureComponent {
render() {
- // The underlayColor is not a valid RN style
- // property, so we have to unset it here.
- const style = {
- ...this.props.style,
- };
- delete style.underlayColor;
+ const { style, ...otherProps } = this.props;
+ const { underlayColor, ...otherStyle } = style;
return (
);
}
diff --git a/components/CategoryPicker/Category.js b/components/CategoryPicker/Category.js
new file mode 100644
index 00000000..8e0cec2b
--- /dev/null
+++ b/components/CategoryPicker/Category.js
@@ -0,0 +1,45 @@
+import React, { useMemo } from 'react';
+import PropTypes from 'prop-types';
+import { connectStyle } from '@shoutem/theme';
+import { Text } from '../Text';
+import { TouchableOpacity } from '../TouchableOpacity';
+import { categoryShape } from './shapes';
+
+function Category({ category, style, isSelected, onPress }) {
+ const textStyle = useMemo(
+ () => [style.category, !isSelected && style.selectedCategory],
+
+ [isSelected, style.category, style.selectedCategory],
+ );
+
+ function handlePress() {
+ if (isSelected) {
+ return;
+ }
+
+ if (onPress) {
+ onPress(category);
+ }
+ }
+
+ return (
+
+ {category.name}
+
+ );
+}
+
+Category.propTypes = {
+ category: categoryShape.isRequired,
+ isSelected: PropTypes.bool,
+ style: PropTypes.object,
+ onPress: PropTypes.func,
+};
+
+Category.defaultProps = {
+ style: {},
+ isSelected: false,
+ onPress: undefined,
+};
+
+export default React.memo(connectStyle('shoutem.ui.Category')(Category));
diff --git a/components/CategoryPicker/CategoryPicker.js b/components/CategoryPicker/CategoryPicker.js
new file mode 100644
index 00000000..f34fa382
--- /dev/null
+++ b/components/CategoryPicker/CategoryPicker.js
@@ -0,0 +1,62 @@
+import React, { useCallback } from 'react';
+import { FlatList } from 'react-native';
+import _ from 'lodash';
+import PropTypes from 'prop-types';
+import { connectStyle } from '@shoutem/theme';
+import { View } from '../View';
+import Category from './Category';
+import { categoryShape } from './shapes';
+
+export function CategoryPicker({
+ categories,
+ onCategorySelected,
+ style,
+ selectedCategory,
+}) {
+ const renderItem = useCallback(
+ ({ item: category }) => (
+
+ ),
+ [selectedCategory.id, onCategorySelected],
+ );
+
+ if (_.size(categories) < 2) {
+ return null;
+ }
+
+ return (
+
+
+
+ );
+}
+
+CategoryPicker.propTypes = {
+ categories: PropTypes.arrayOf(categoryShape),
+ selectedCategory: categoryShape,
+ style: PropTypes.object,
+ onCategorySelected: PropTypes.func,
+};
+
+CategoryPicker.defaultProps = {
+ categories: [],
+ style: {},
+ selectedCategory: undefined,
+ onCategorySelected: undefined,
+};
+
+export default React.memo(
+ connectStyle('shoutem.ui.CategoryPicker')(CategoryPicker),
+);
diff --git a/components/CategoryPicker/index.js b/components/CategoryPicker/index.js
new file mode 100644
index 00000000..2a8495e3
--- /dev/null
+++ b/components/CategoryPicker/index.js
@@ -0,0 +1 @@
+export { default as CategoryPicker } from './CategoryPicker';
diff --git a/components/CategoryPicker/shapes.js b/components/CategoryPicker/shapes.js
new file mode 100644
index 00000000..89d56db8
--- /dev/null
+++ b/components/CategoryPicker/shapes.js
@@ -0,0 +1,7 @@
+import PropTypes from 'prop-types';
+
+export const categoryShape = PropTypes.shape({
+ id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
+ name: PropTypes.string.isRequired,
+ description: PropTypes.string,
+});
diff --git a/components/DateTimePicker.js b/components/DateTimePicker.js
index bde0530a..1757a4f2 100644
--- a/components/DateTimePicker.js
+++ b/components/DateTimePicker.js
@@ -198,13 +198,14 @@ class DateTimePicker extends PureComponent {
}
DateTimePicker.propTypes = {
+ style: PropTypes.object.isRequired,
cancelButtonText: PropTypes.string,
confirmButtonText: PropTypes.string,
is24Hour: PropTypes.bool,
mode: PropTypes.oneOf(Object.values(DATEPICKER_MODES)),
- onValueChanged: PropTypes.func,
textValue: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]),
+ onValueChanged: PropTypes.func,
};
DateTimePicker.defaultProps = {
@@ -212,7 +213,9 @@ DateTimePicker.defaultProps = {
confirmButtonText: 'Confirm',
is24Hour: false,
mode: 'datetime',
+ textValue: undefined,
value: new Date(),
+ onValueChanged: undefined,
};
const StyledDateTimePicker = connectStyle('shoutem.ui.DateTimePicker')(
diff --git a/components/DropDownMenu/DropDownMenu.js b/components/DropDownMenu/DropDownMenu.js
index a2b92b27..d0dc0ac2 100644
--- a/components/DropDownMenu/DropDownMenu.js
+++ b/components/DropDownMenu/DropDownMenu.js
@@ -10,31 +10,8 @@ import { View } from '../View';
import { DropDownModal } from './DropDownModal';
const modalSpecificProps = ['visible', 'onClose'];
-const dropDownMenuPropTypes = {
- ..._.omit(DropDownModal.propTypes, modalSpecificProps),
-};
class DropDownMenu extends PureComponent {
- /**
- * @see DropDownModal.propTypes
- */
- static propTypes = {
- /**
- * Icon displayed on dropdown menu button
- */
- iconName: PropTypes.string,
- /**
- * Whether the text should be displayed next to dropdown icon or not
- */
- showSelectedOption: PropTypes.bool,
- ...dropDownMenuPropTypes,
- };
-
- static defaultProps = {
- iconName: 'drop-down',
- showSelectedOption: true,
- };
-
constructor(props) {
super(props);
@@ -105,6 +82,26 @@ class DropDownMenu extends PureComponent {
}
}
+/**
+ * @see DropDownModal.propTypes
+ */
+DropDownMenu.propTypes = {
+ /**
+ * Icon displayed on dropdown menu button
+ */
+ iconName: PropTypes.string,
+ /**
+ * Whether the text should be displayed next to dropdown icon or not
+ */
+ showSelectedOption: PropTypes.bool,
+ ..._.omit(DropDownModal.propTypes, modalSpecificProps),
+};
+
+DropDownMenu.defaultProps = {
+ iconName: 'drop-down',
+ showSelectedOption: true,
+};
+
const StyledDropDownMenu = connectStyle('shoutem.ui.DropDownMenu')(
DropDownMenu,
);
diff --git a/components/DropDownMenu/DropDownModal.js b/components/DropDownMenu/DropDownModal.js
index a5574f80..2ac1ce14 100644
--- a/components/DropDownMenu/DropDownModal.js
+++ b/components/DropDownMenu/DropDownModal.js
@@ -15,57 +15,6 @@ import { View } from '../View';
const window = Dimensions.get('window');
class DropDownModal extends PureComponent {
- static propTypes = {
- /**
- * Callback that is called when dropdown option is selected
- */
- onOptionSelected: PropTypes.func,
- /**
- * Collection of objects which will be shown as options in DropDownMenu
- */
- options: PropTypes.array.isRequired,
- /**
- * Selected option that will be shown.
- */
- selectedOption: PropTypes.any.isRequired,
- /**
- * Key name that represents option's string value,
- * and it will be displayed to the user in the UI
- */
- titleProperty: PropTypes.string.isRequired,
- /**
- * Key name that represents option's value
- */
- valueProperty: PropTypes.string.isRequired,
- /**
- * Number of options shown without scroll.
- * Can be set trough DropDown style.visibleOptions.
- * Prop definition overrides style.
- */
- visibleOptions: PropTypes.number,
- /**
- * Optional render function, for every item in the list.
- * Input parameter should be shaped as one of the items from the
- * options object
- */
- renderOption: PropTypes.func,
- /**
- * Visibility flag, controling the modal visibility
- */
- visible: PropTypes.bool,
- /**
- * Callback that is called when modal should be closed
- */
- onClose: PropTypes.func,
- style: PropTypes.object,
- };
-
- static defaultProps = {
- renderOption: (option, titleProperty) => (
- {option[titleProperty].toUpperCase()}
- ),
- };
-
static DEFAULT_VISIBLE_OPTIONS = 8;
constructor(props) {
@@ -111,14 +60,16 @@ class DropDownModal extends PureComponent {
close() {
const { onClose } = this.props;
- if (onClose) {
+ if (_.isFunction(onClose)) {
onClose();
}
}
emitOnOptionSelectedEvent(option) {
- if (this.props.onOptionSelected) {
- this.props.onOptionSelected(option);
+ const { onOptionSelected } = this.props;
+
+ if (_.isFunction(onOptionSelected)) {
+ onOptionSelected(option);
}
}
@@ -299,6 +250,57 @@ class DropDownModal extends PureComponent {
}
}
+DropDownModal.propTypes = {
+ /**
+ * Collection of objects which will be shown as options in DropDownMenu
+ */
+ options: PropTypes.array.isRequired,
+ /**
+ * Selected option that will be shown.
+ */
+ selectedOption: PropTypes.any.isRequired,
+ style: PropTypes.object.isRequired,
+ /**
+ * Key name that represents option's string value,
+ * and it will be displayed to the user in the UI
+ */
+ titleProperty: PropTypes.string.isRequired,
+ /**
+ * Optional render function, for every item in the list.
+ * Input parameter should be shaped as one of the items from the
+ * options object
+ */
+ renderOption: PropTypes.func,
+ /**
+ * Visibility flag, controling the modal visibility
+ */
+ visible: PropTypes.bool,
+ /**
+ * Number of options shown without scroll.
+ * Can be set trough DropDown style.visibleOptions.
+ * Prop definition overrides style.
+ */
+ visibleOptions: PropTypes.number,
+ /**
+ * Callback that is called when modal should be closed
+ */
+ onClose: PropTypes.func,
+ /**
+ * Callback that is called when dropdown option is selected
+ */
+ onOptionSelected: PropTypes.func,
+};
+
+DropDownModal.defaultProps = {
+ renderOption: (option, titleProperty) => (
+ {option[titleProperty].toUpperCase()}
+ ),
+ visible: false,
+ visibleOptions: undefined,
+ onClose: undefined,
+ onOptionSelected: undefined,
+};
+
const StyledModal = connectStyle('shoutem.ui.DropDownModal')(DropDownModal);
export { StyledModal as DropDownModal };
diff --git a/components/EmptyStateView.js b/components/EmptyStateView.js
index b203c331..cc4b7528 100644
--- a/components/EmptyStateView.js
+++ b/components/EmptyStateView.js
@@ -8,11 +8,6 @@ import { Subtitle, Text } from './Text';
import { View } from './View';
class EmptyStateView extends PureComponent {
- static defaultProps = {
- retryButtonTitle: 'TRY AGAIN',
- icon: 'error',
- };
-
constructor(props) {
super(props);
@@ -20,13 +15,15 @@ class EmptyStateView extends PureComponent {
}
onRetry() {
- this.props.onRetry();
+ const { onRetry } = this.props;
+
+ onRetry();
}
renderRetryButton() {
const { retryButtonTitle } = this.props;
- // Show retry button at the bottom only if there is an onRetry action passed.
+ // Show retry button at the bottom only if there is an onRetry action passed
return (