Skip to content

Commit

Permalink
feat(Services): Improve user experience of service search
Browse files Browse the repository at this point in the history
  • Loading branch information
adlk committed Jan 3, 2018
1 parent 306c331 commit 7e784c6
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 65 deletions.
20 changes: 12 additions & 8 deletions src/components/settings/recipes/RecipesDashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ const messages = defineMessages({
id: 'settings.recipes.headline',
defaultMessage: '!!!Available Services',
},
searchService: {
id: 'settings.searchService',
defaultMessage: '!!!Search service',
},
mostPopularRecipes: {
id: 'settings.recipes.mostPopular',
defaultMessage: '!!!Most popular',
Expand Down Expand Up @@ -81,13 +85,7 @@ export default class RecipesDashboard extends Component {
return (
<div className="settings__main">
<div className="settings__header">
<SearchInput
className="settings__search-header"
defaultValue={intl.formatMessage(messages.headline)}
onChange={e => searchRecipes(e)}
onReset={() => resetSearch()}
throttle
/>
<h1>{intl.formatMessage(messages.headline)}</h1>
</div>
<div className="settings__body recipes">
{serviceStatus.length > 0 && serviceStatus.includes('created') && (
Expand All @@ -101,7 +99,13 @@ export default class RecipesDashboard extends Component {
</Infobox>
</Appear>
)}
{/* {!searchNeedle && ( */}
<SearchInput
placeholder={intl.formatMessage(messages.searchService)}
onChange={e => searchRecipes(e)}
onReset={() => resetSearch()}
autoFocus
throttle
/>
<div className="recipes__navigation">
<Link
to="/settings/recipes"
Expand Down
42 changes: 35 additions & 7 deletions src/components/settings/services/ServicesDashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,18 @@ const messages = defineMessages({
id: 'settings.services.headline',
defaultMessage: '!!!Your services',
},
searchService: {
id: 'settings.searchService',
defaultMessage: '!!!Search service',
},
noServicesAdded: {
id: 'settings.services.noServicesAdded',
defaultMessage: '!!!You haven\'t added any services yet.',
},
noServiceFound: {
id: 'settings.recipes.nothingFound',
defaultMessage: '!!!Sorry, but no service matched your search term.',
},
discoverServices: {
id: 'settings.services.discoverServices',
defaultMessage: '!!!Discover services',
Expand Down Expand Up @@ -53,7 +61,13 @@ export default class ServicesDashboard extends Component {
servicesRequestFailed: PropTypes.bool.isRequired,
retryServicesRequest: PropTypes.func.isRequired,
status: MobxPropTypes.arrayOrObservableArray.isRequired,
searchNeedle: PropTypes.string,
};

static defaultProps = {
searchNeedle: '',
}

static contextTypes = {
intl: intlShape,
};
Expand All @@ -69,20 +83,24 @@ export default class ServicesDashboard extends Component {
servicesRequestFailed,
retryServicesRequest,
status,
searchNeedle,
} = this.props;
const { intl } = this.context;

return (
<div className="settings__main">
<div className="settings__header">
<SearchInput
className="settings__search-header"
defaultValue={intl.formatMessage(messages.headline)}
onChange={needle => filterServices({ needle })}
onReset={() => resetFilter()}
/>
<h1>{intl.formatMessage(messages.headline)}</h1>
</div>
<div className="settings__body">
{!isLoading && (
<SearchInput
placeholder={intl.formatMessage(messages.searchService)}
onChange={needle => filterServices({ needle })}
onReset={() => resetFilter()}
autoFocus
/>
)}
{!isLoading && servicesRequestFailed && (
<div>
<Infobox
Expand Down Expand Up @@ -121,7 +139,7 @@ export default class ServicesDashboard extends Component {
</Appear>
)}

{!isLoading && services.length === 0 && (
{!isLoading && services.length === 0 && !searchNeedle && (
<div className="align-middle settings__empty-state">
<p className="settings__empty-text">
<span className="emoji">
Expand All @@ -132,6 +150,16 @@ export default class ServicesDashboard extends Component {
<Link to="/settings/recipes" className="button">{intl.formatMessage(messages.discoverServices)}</Link>
</div>
)}
{!isLoading && services.length === 0 && searchNeedle && (
<div className="align-middle settings__empty-state">
<p className="settings__empty-text">
<span className="emoji">
<img src="./assets/images/emoji/dontknow.png" alt="" />
</span>
{intl.formatMessage(messages.noServiceFound)}
</p>
</div>
)}
{isLoading ? (
<Loader />
) : (
Expand Down
48 changes: 18 additions & 30 deletions src/components/ui/SearchInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,46 @@ import { debounce } from 'lodash';
export default class SearchInput extends Component {
static propTypes = {
value: PropTypes.string,
defaultValue: PropTypes.string,
placeholder: PropTypes.string,
className: PropTypes.string,
onChange: PropTypes.func,
onReset: PropTypes.func,
name: PropTypes.string,
throttle: PropTypes.bool,
throttleDelay: PropTypes.number,
autoFocus: PropTypes.bool,
};

static defaultProps = {
value: '',
defaultValue: '',
placeholder: '',
className: '',
name: uuidv1(),
throttle: false,
throttleDelay: 250,
onChange: () => null,
onReset: () => null,
autoFocus: false,
}

constructor(props) {
super(props);

this.state = {
value: props.value || props.defaultValue,
value: props.value,
};

this.throttledOnChange = debounce(this.throttledOnChange, this.props.throttleDelay);
}

componentDidMount() {
const { autoFocus } = this.props;

if (autoFocus) {
this.input.focus();
}
}

onChange(e) {
const { throttle, onChange } = this.props;
const { value } = e.target;
Expand All @@ -52,43 +62,23 @@ export default class SearchInput extends Component {
}
}

onClick() {
const { defaultValue } = this.props;
const { value } = this.state;

if (value === defaultValue) {
this.setState({ value: '' });
}

this.input.focus();
}

onBlur() {
const { defaultValue } = this.props;
const { value } = this.state;

if (value === '') {
this.setState({ value: defaultValue });
}
}

throttledOnChange(e) {
const { onChange } = this.props;

onChange(e);
}

reset() {
const { defaultValue, onReset } = this.props;
this.setState({ value: defaultValue });
const { onReset } = this.props;
this.setState({ value: '' });

onReset();
}

input = null;

render() {
const { className, name, defaultValue } = this.props;
const { className, name, placeholder } = this.props;
const { value } = this.state;

return (
Expand All @@ -101,18 +91,16 @@ export default class SearchInput extends Component {
<label
htmlFor={name}
className="mdi mdi-magnify"
onClick={() => this.onClick()}
/>
<input
name={name}
type="text"
placeholder={placeholder}
value={value}
onChange={e => this.onChange(e)}
onClick={() => this.onClick()}
onBlur={() => this.onBlur()}
ref={(ref) => { this.input = ref; }}
/>
{value !== defaultValue && value.length > 0 && (
{value.length > 0 && (
<span
className="mdi mdi-close-circle-outline"
onClick={() => this.reset()}
Expand Down
1 change: 1 addition & 0 deletions src/containers/settings/ServicesScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export default class ServicesScreen extends Component {
goTo={router.push}
servicesRequestFailed={services.allServicesRequest.wasExecuted && services.allServicesRequest.isError}
retryServicesRequest={() => services.allServicesRequest.reload()}
searchNeedle={services.filterNeedle}
/>
);
}
Expand Down
1 change: 1 addition & 0 deletions src/i18n/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"sidebar.unmuteApp": "Enable notifications & audio",
"services.welcome": "Welcome to Franz",
"services.getStarted": "Get started",
"settings.searchService": "Search service",
"settings.account.headline": "Account",
"settings.account.headlineSubscription": "Your subscription",
"settings.account.headlineUpgrade": "Upgrade your account & support Franz",
Expand Down
16 changes: 16 additions & 0 deletions src/styles/searchInput.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,20 @@
.search-input {
width: 100%;
height: auto;
display: flex;
align-items: center;
padding: 0 10px;
border-radius: 30px;
background: $theme-gray-lightest;
padding: 5px 10px;
@extend %headline;
color: $theme-gray-light;

input {
padding-left: 10px;
background: none;
border: 0;
flex: 1;
color: $theme-gray-light;
}
}
22 changes: 2 additions & 20 deletions src/styles/settings.scss
Original file line number Diff line number Diff line change
Expand Up @@ -129,26 +129,8 @@
}
}

.settings__search-header {
display: flex;
align-items: center;
padding: 0 10px;
border-radius: $theme-border-radius;
transition: background $theme-transition-time;
@extend %headline;
font-size: 22px;

&:hover {
background: darken($theme-gray-lighter, 5%);
}

input {
padding-left: 10px;
background: none;
border: 0;
flex: 1;
@extend %headline;
}
.search-input {
margin-bottom: 30px;
}

&__options {
Expand Down

0 comments on commit 7e784c6

Please sign in to comment.