Transforming An Existing React Front End Using KendoReact Components

KendoReact components come in four different flavors covering all modern front-end frameworks – jQuery, Angular, React and Vue. They offer widgets that can be used right out of the box, while also offering endless customizability to fit all existing use-cases.
Here we will see one such example of migrating an existing React front end from a multitude of libraries to a unified system using KendoReact components. This allows the streamlining and unification of the codebase, easing future development tasks.
What are the benefits of KendoReact?
There are numerous benefits of migrating to KendoReact. The first one is replacing all the different libraries and external packages with a unified framework offering everything in one package. And KendoReact does that by bringing a selection of components that are easy to use in all use-cases – from creating modern layouts to building data-rich applications.
The components offered by KendoReact make application building easier, faster and more efficient. They are composable and precisely configurable to give developers the ability to work with them just as they would any other React component and – of course – they support both controlled and uncontrolled states.
Finally using KendoReact offers a stable framework that gets regular updates and is always on top of new changes in both the React and JavaScript worlds, while bringing easy maintainability for future-proofing.
The migration in details
We will focus on three specific cases – one involving the KendoReact Grid component. The Grid allows us to create a unified interface for displaying, sorting and filtering tabled data. The second case showcases several of the layout and dialog components. The Drawer, Window and AppBar components are ready-to-use to create a user interface, fulfilling all the expectations for a modern application. The final is an example of the rich Forms support included in KendoReact.
Users table
Here in the old interface, we were using a table along with a custom filter component. By introducing the KendoReact Grid component we are able to do away with the custom filter and use the included functionality to replace it. Implementing sorting is also painless, as it is a built-in capability of the Grid.
Old interface
New interface
And here is the code for the above interface. It is a single Grid
which has all the needed functionality of the original example, but it’s all packaged into one – including filters, sorting and other actions needed to replace the original interface.
Each data column is specified by the GridColumn
component. The actions at the top of the grid are specified by the GridToolbar
component.
Scroll to the end of this article to see what the code looks like.
Layout
The AppBar component will replace the existing navbar at the top of all pages. It allows for easy unification of the brand identity and navigation items that are present throughout the web app. The Drawer component is used as side navigation which gives a clean interface to navigation items that are used less often and thus do not need to be visible at all times.
Old interface
New interface
Scroll to the end of this article to see what the code looks like.
Form in Window
Finally, we will showcase the Form and Window components. Here they are used as a pop-up interface for adding and editing existing Users. The Form offers out-of-the-box support for creating complex fields for all types of inputs, along with custom ones.
Old interface
New interface
Scroll to the end of this article to see what the code looks like.
In summary
KendoReact is a great choice for all types of front-end use-cases – from simple presentation pages to extensive data-rich web applications. The library also comes in three other flavors, covering all modern front-end libraries – jQuery, Angular, and Vue. The use of KendoReact in our specific case allows us to transition from a scattering of front-end libraries and components to a unified component library which eases development tasks as well as allowing a unified and fully functional user interface.
This article was written by Kalin Koychev, KendoReact guru and Web Developer at Proxiad.
Users table:
<Grid
data={orderBy(
filterBy(this.state.mappedUsers, this.state.filter),
this.state.sort
)}
scrollable="none"
filterable
sortable={true}
sort={this.state.sort}
onSortChange={(e) => {
this.setState({
sort: e.sort,
});
}}
filter={this.state.filter}
onFilterChange={(e) => {
this.setState({
filter: e.filter,
});
}}
>
<GridToolbar>
<ChipList
selection="single"
data={chipData}
onDataChange={this.handleChipListChange}
chip={(props) => <Chip onClick={this.handleChipListChange} {...props} />}
/>
<div>
<Button icon="close" look="flat" onClick={this.handleFilterReset}>
Clear Filters
</Button>
<ConfigurationPropertiesContext.Consumer>
{({ internalUserManagement }) =>
internalUserManagement && (
<Button
icon="plus-outline"
look="flat"
onClick={() => this.handleOpenModal(USER_CREATE_MODE)}
>
Add new user
</Button>
)
}
</ConfigurationPropertiesContext.Consumer>
</div>
</GridToolbar>
<GridColumn
title="Name"
field="fullName"
width="240px"
cell={(props) => (
<td>
<Avatar
shape="rounded"
style={{
marginRight: ".75rem",
width: "40px",
height: "40px",
}}
>
<img
src={UserService.getUserProfilePictureUrlByEmail(
props.dataItem.email
)}
/>
</Avatar>
{props.dataItem.fullName}
</td>
)}
/>
<GridColumn field="email" title="e-mail" />
<GridColumn
field="mappedRoles"
title="Role"
filterCell={dropdownFilterCell(
this.state.roles.map((role) => role.name),
"Any"
)}
cell={(props) => <td>{props.dataItem[props.field]}</td>}
/>
<GridColumn
field="managerName"
title="Manager"
filterCell={dropdownFilterCell(
this.state.managers.map(
(manager) => `${manager.firstName} ${manager.lastName}`
),
"Any manager"
)}
/>
<GridColumn
title="Actions"
width="120px"
filterable={false}
sortable={false}
cell={(props) => {
const user = props.dataItem;
const principal = this.props.user;
return (
<td>
<Tooltip openDelay={100} anchorElement="target" position="top">
<Icon
name="edit"
title="Edit User"
onClick={() => this.handleOpenModal(USER_EDIT_MODE, user)}
/>
<ConfigurationPropertiesContext.Consumer>
{({ internalUserManagement }) =>
internalUserManagement &&
principal.id !== user.id && (
<Icon
name="delete"
title="Delete User"
onClick={() => this.handleOpenModal(USER_DELETE_MODE, user)}
/>
)
}
</ConfigurationPropertiesContext.Consumer>
<RunAsBtn user={props.dataItem} principal={principal} />
</Tooltip>
</td>
);
}}
/>
</Grid>
data
property is used to pass all the data to the GridorderBy
andfilterBy
are used to manage the displaying of the datasortable
,sort
onSortChange
are properties needed for the sorting of the Gridfilterable
,filter
andonFilterChange
are properties needed for the filtering of the GridGridToolbar
allows for adding a toolbar containing different actions to the top of the Grid. In this case, we have added aChipList
used for additional filters outside of the existing columns. There are also two buttons – one to clear all filters – and another with a custom action – opening a window for adding new users.GridColumn
components, which specify each column of the Grid. Some only need afield
andtitle
property that tell the Grid which data field to use and what the column title would be. Others use thecell
property to render a custom view in the table cell for the items in that columnfilterCell
is used to pass a custom filtering component for certain columns.
Layout:
First, we will showcase the AppDrawer
which replaces the top navigation bar.
<AppBar className="row">
<AppBarSection>
<Link to="/" className="navbar-brand">
<img src="/images/logo.png" alt="Proxiad" width="120" height="33" />
</Link>
</AppBarSection>
<AppBarSection>
<nav className="navbar-nav">
{AuthenticationService.hasPermission(user, PERMISSION_SELF_ASSESS) && (
<NavLink exact to="/mygoals" activeClassName="selected">
My Goals
</NavLink>
)}
{AuthenticationService.hasPermission(user, PERMISSION_SELF_ASSESS) && (
<NavLink exact to="/appraisal" activeClassName="selected">
Appraisal
</NavLink>
)}
{AuthenticationService.hasPermission(user, PERMISSION_SELF_ASSESS) && (
<NavLink exact to="/appraisal/history" activeClassName="selected">
History
</NavLink>
)}
{AuthenticationService.hasPermission(
user,
PERMISSION_ASSESS_EMPLOYEES
) && (
<NavLink exact to="/manager" activeClassName="selected">
Manager
</NavLink>
)}
{AuthenticationService.hasPermission(
user,
PERMISSION_MANAGE_ORGANIZATIONAL_GOALS
) && (
<NavLink
exact
to="/goals/template/organizational"
activeClassName="selected"
>
Organizational Goals
</NavLink>
)}
{AuthenticationService.hasPermission(
user,
PERMISSION_MANAGE_GOAL_TEMPLATES
) && (
<NavLink exact to="/goals/template" activeClassName="selected">
Goals Template
</NavLink>
)}
{AuthenticationService.hasPermission(user, PERMISSION_MANAGE_PROCESS) && (
<NavLink exact to="/processes" activeClassName="selected">
Processes
</NavLink>
)}
</nav>
</AppBarSection>
<AppBarSpacer />
{(AuthenticationService.hasPermission(user, PERMISSION_MANAGE_USERS) ||
AuthenticationService.hasPermission(user, PERMISSION_MANAGE_ROLES) ||
AuthenticationService.hasPermission(user, PERMISSION_MANAGE_TEAMS)) && (
<AppBarSection className="actions">
<ButtonGroup>
<DropDownButton
ref={settingsDropdown}
icon="cog"
look="flat"
onItemClick={(event) => history.push(event.item.to)}
>
{AuthenticationService.hasPermission(
user,
PERMISSION_MANAGE_USERS
) && (
<DropDownButtonItem
text="Users"
iconClass="fa fa-users-cog fa-fw"
to="/admin/users"
/>
)}
{AuthenticationService.hasPermission(
user,
PERMISSION_MANAGE_ROLES
) && (
<DropDownButtonItem
text="Roles"
iconClass="fa fa-cogs fa-fw"
to="/admin/roles"
/>
)}
{AuthenticationService.hasPermission(
user,
PERMISSION_MANAGE_TEAMS
) && (
<DropDownButtonItem
text="Teams"
iconClass="fa fa-users fa-fw"
to="/admin/teams"
/>
)}
</DropDownButton>
<Button
icon="arrow-60-down"
look="flat"
onClick={() => {
if (!document.querySelector(".k-popup .k-list"))
settingsDropdown.current.mainButton.click();
}}
/>
</ButtonGroup>
</AppBarSection>
)}
{user && (
<AppBarSection>
<ButtonGroup>
<Button
imageUrl={userProfilePicture}
look="flat"
onClick={() => setShowUserProfile(!showUserProfile)}
/>
<Button
icon="arrow-60-down"
look="flat"
onClick={() => setShowUserProfile(!showUserProfile)}
/>
</ButtonGroup>
</AppBarSection>
)}
</AppBar>
- The
AppBar
offers an easy-to-use navigation component, which can contain any custom configurations. It is made up ofAppBarSection
andAppBarSpacer
components. Each section separates different items inside theAppBar
, while theAppBarSpacer
adds extra separation between sections.
The second part of the above interface is the Drawer
component, which replaces the side navigation.
<Drawer
expanded={showUserProfile}
position={"end"}
mode={"overlay"}
onOverlayClick={() => setShowUserProfile(false)}
onSelect={onSelect}
items={items}
item={(props) => {
return (
<DrawerItem {...props}>
{props.user && (
<div>
<Avatar type="image" size="medium" shape="circle">
<img src={userProfilePicture} alt="User avatar" />
</Avatar>
{user.firstName} {user.lastName} <br />
{user.title} <br />
{user.email} <br />
{`${user.roles.map((r) => getRoleName(r)).join(" / ")}`} <br />
</div>
)}
{!props.user && <div>{props.text}</div>}
{props.versiondata && (
<div>
{props.versiondata.git
? `Ver.: ${props.versiondata.build.version} / Rev.${props.versiondata.git.commit.id} / ${props.versiondata.git.commit.time}`
: `Ver.: ${props.versiondata.build.version}`}
</div>
)}
</DrawerItem>
);
}}
>
<DrawerNavigation></DrawerNavigation>
</Drawer>
Drawer
is used for side navigationexpanded
property sets the display of the drawerposition
andmode
control the display styleonOverlayClick
controls the behavior when clicking outside of the drawer navigationonSelect
controls the functionality of clicking an item in the draweritems
is the total items to be displayed in the draweritem
allows for custom rendering, which in this case is used to display different items, as in our use-case we have a few differently displayed items from the others – it returns aDrawerItem
component for each item.
Form in window:
And here is the code – first for the form itself, which uses the Form
(aliased as KendoForm
) and FormElement
components.
<KendoForm
onSubmit={handleSubmit}
render={(formRenderProps) => (
<FormElement>
<fieldset>
<Field name={"firstName"} component={Input} label={"First Name"} />
<Field name={"lastName"} component={Input} label={"Last Name"} />
</fieldset>
<Field name={"title"} component={Input} label={"Position Title"} />
<fieldset>
<Field name={"username"} component={Input} label={"Username"} />
<Field name={"email"} component={Input} label={"Email"} />
</fieldset>
<fieldset>
<Field name={"password"} component={Input} label={"Password"} />
<Field
name={"confirmPassword"}
component={Input}
label={"Confirm Password"}
disabled={true}
/>
</fieldset>
<DropDownList label="Manager"></DropDownList>
<DropDownList label="Teams"></DropDownList>
<KendoLabel>Roles</KendoLabel>
<fieldset>
{hcRoles.map((role, i) => (
<KendoLabel style={{ marginBottom: "5px" }} key={i}>
<Switch />
{role}
</KendoLabel>
))}
</fieldset>
<div>
<fieldset style={{ display: "flex", flexWrap: "wrap" }}>
<KendoLabel style={{ marginRight: "5px" }}>
<Switch defaultChecked={true} />
Active
</KendoLabel>
<KendoLabel>
<Switch />
Limited visibility
</KendoLabel>
</fieldset>
<fieldset style={{ display: "flex", flexWrap: "wrap" }}>
<KendoButton style={{ marginRight: "5px" }}>Cancel</KendoButton>
<KendoButton
type={"submit"}
primary={true}
onClick={formRenderProps.onSubmit}
>
Submit
</KendoButton>
</fieldset>
</div>
</FormElement>
)}
/>
- The form containing all different form fields. The
Field
component is used and it’s passed the type of field as acomponent
prop.
And the code for the pop-up which employs the Window
component:
<Window
title={"Add new user"}
onClose={this.handleCloseModal}
initialTop={0}
initialHeight={660}
initialWidth={
window.screen.width > 750
? window.screen.width / 2
: window.screen.width - 50
}
>
{(this.state.mode === USER_CREATE_MODE ||
this.state.mode === USER_EDIT_MODE) && (
<EditUserForm
user={this.state.selectedUser}
handleClose={this.handleCloseModal}
handleFormSubmit={this.handleSubmit}
users={this.state.users}
roles={this.state.roles}
teams={this.state.teams}
/>
)}
</Window>
Window
is a component that creates a pop-up window. The content of the component is rendered inside the displayed window, in this caseEditUserForm
, which is the custom form.