Skip to content

Commit

Permalink
Improve select user component to show and allow filtering by sign off (
Browse files Browse the repository at this point in the history
  • Loading branch information
tomaskikutis authored Aug 17, 2023
1 parent 18f1b8d commit 67cee3c
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 45 deletions.
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
"sass-loader": "6.0.6",
"shortid": "2.2.8",
"style-loader": "0.20.2",
"superdesk-ui-framework": "^3.0.53",
"superdesk-ui-framework": "^3.0.54",
"ts-loader": "3.5.0",
"tslint": "5.11.0",
"typescript": "~4.9.5",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -346,12 +346,13 @@ export default class UserActivityWidget extends React.Component<IProps, IState>
>
<form className="search-box__content">
<SelectUser
selectedUserId={this.props.user?._id}
selectedUserId={this.props.user?._id ?? null}
autoFocus={false}
onSelect={(user) => {
this.props.onUserChange(user);
}}
horizontalSpacing={true}
clearable={false}
/>
</form>
</div>
Expand Down
2 changes: 2 additions & 0 deletions scripts/core/superdesk-api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1176,6 +1176,8 @@ declare module 'superdesk-api' {
disabled?: boolean;
autoFocus?: boolean;
horizontalSpacing?: boolean;
valueTemplate?: React.ComponentType<{option: IUser}>;
clearable: boolean;
}

export interface IDropdownTreeGroup<T> {
Expand Down
112 changes: 72 additions & 40 deletions scripts/core/ui/components/SelectUser.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,62 @@
/* eslint-disable react/no-multi-comp */
import React from 'react';
import {IPropsSelectUser, IUser, IRestApiResponse} from 'superdesk-api';
import {gettext, getUserSearchMongoQuery} from 'core/utils';
import {UserAvatar} from 'apps/users/components/UserAvatar';
import {SelectWithTemplate} from 'superdesk-ui-framework/react';
import {SelectWithTemplate, Spacer} from 'superdesk-ui-framework/react';
import {httpRequestJsonLocal} from 'core/helpers/network';
import {SuperdeskReactComponent} from 'core/SuperdeskReactComponent';

interface IState {
selectedUser: IUser | null | 'loading';
}

const itemTemplate = (props: {option: IUser}) => {
const user: IUser | null = props.option;

return user == null
? (
<div>
{gettext('Select a user')}
</div>
)
: (
<Spacer h gap="8" noWrap justifyContent="start">
<Spacer h gap="8" noWrap justifyContent="start">
<div>
<UserAvatar user={user} displayStatus={true} />
</div>

<Spacer v gap="4" noWrap>
<div>{user.display_name}</div>
<div style={{fontSize: '1.2rem'}}>@{user.username}</div>
</Spacer>

</Spacer>

<div style={{fontSize: '1.2rem'}}>{user.sign_off}</div>
</Spacer>
);
};

const valueTemplateDefault = (props: {option: IUser}) => {
const user: IUser | null = props.option;

return user == null
? (
<div>
{gettext('Select a user')}
</div>
)
: (
<Spacer h gap="8" justifyContent="start" noGrow>
<UserAvatar user={user} displayStatus={true} />

{user.display_name}
</Spacer>
);
};

export class SelectUser extends SuperdeskReactComponent<IPropsSelectUser, IState> {
constructor(props: IPropsSelectUser) {
super(props);
Expand Down Expand Up @@ -37,24 +84,26 @@ export class SelectUser extends SuperdeskReactComponent<IPropsSelectUser, IState
}

componentDidUpdate(prevProps: IPropsSelectUser) {
// state.user needs to be updated if props.selectedUserId changes
if (this.props.selectedUserId == null && this.state.selectedUser != null) {
// eslint-disable-next-line react/no-did-update-set-state
this.setState({selectedUser: null});
} else if (
this.state.selectedUser === 'loading'
|| this.state.selectedUser?._id !== this.props.selectedUserId
) {
// eslint-disable-next-line react/no-did-update-set-state
this.setState({selectedUser: 'loading'});

this.asyncHelpers.httpRequestJsonLocal<IUser>({
method: 'GET',
path: `/users/${this.props.selectedUserId}`,
}).then((selectedUser) => {
if (prevProps.selectedUserId !== this.props.selectedUserId) {
// state.user needs to be updated if props.selectedUserId changes
if (this.props.selectedUserId == null) {
// eslint-disable-next-line react/no-did-update-set-state
this.setState({selectedUser});
});
this.setState({selectedUser: null});
} else if (
this.state.selectedUser === 'loading'
|| this.state.selectedUser?._id !== this.props.selectedUserId
) {
// eslint-disable-next-line react/no-did-update-set-state
this.setState({selectedUser: 'loading'});

this.asyncHelpers.httpRequestJsonLocal<IUser>({
method: 'GET',
path: `/users/${this.props.selectedUserId}`,
}).then((selectedUser) => {
// eslint-disable-next-line react/no-did-update-set-state
this.setState({selectedUser});
});
}
}
}

Expand All @@ -63,6 +112,8 @@ export class SelectUser extends SuperdeskReactComponent<IPropsSelectUser, IState
return null;
}

const valueTemplate = this.props.valueTemplate != null ? this.props.valueTemplate : valueTemplateDefault;

return (
<SelectWithTemplate
label={gettext('Select a user')}
Expand Down Expand Up @@ -108,27 +159,8 @@ export class SelectUser extends SuperdeskReactComponent<IPropsSelectUser, IState
this.props.onSelect(user);
}}
getLabel={(option) => option.display_name}
itemTemplate={
(props) => {
const user = props.option;

return user == null
? (
<div>
{gettext('Select a user')}
</div>
)
: (
<div style={{display: 'flex', alignItems: 'center'}}>
<UserAvatar user={user} displayStatus={true} />
<div style={{marginLeft: 14, padding: '4px 0'}}>
<div>{user.display_name}</div>
<div style={{fontSize: '1.2rem'}}>@{user.username}</div>
</div>
</div>
);
}
}
itemTemplate={itemTemplate}
valueTemplate={valueTemplate}
areEqual={(a, b) => a._id === b._id}
autoFocus={this.props.autoFocus}
autoOpen={this.state.selectedUser == null}
Expand All @@ -137,7 +169,7 @@ export class SelectUser extends SuperdeskReactComponent<IPropsSelectUser, IState
noResultsFoundMessage={gettext('No results found.')}
filterPlaceholder={gettext('Search...')}
data-test-id="select-user-dropdown"
required
required={!(this.props.clearable ?? true)}
/>
);
}
Expand Down
1 change: 1 addition & 0 deletions scripts/core/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ export function getUserSearchMongoQuery(searchString: string) {
{first_name: {$regex: searchString, $options: '-i'}},
{last_name: {$regex: searchString, $options: '-i'}},
{email: {$regex: searchString, $options: '-i'}},
{sign_off: {$regex: searchString, $options: '-i'}},
],
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export function getMarkForUserModal(options: {
onSelect={(selectedUser) => this.setState({selectedUserId: selectedUser._id})}
selectedUserId={this.state.selectedUserId}
autoFocus={true}
clearable={false}
/>
</ModalBody>
<ModalFooter>
Expand Down

0 comments on commit 67cee3c

Please sign in to comment.