What is Jimu?
Jimu is the core JavaScript framework that powers ArcGIS Experience Builder. It serves as the foundation that makes Experience Builder extensible and customizable. It provides:
- Extensibility framework: The architecture that allows you to create custom components
- Configuration system: A way to customize experiences without coding
- Package library: A collection of libraries for different development needs
- Development tools: Utilities and patterns for building applications
Jimu is designed with modularity in mind, allowing you to use only what you need while maintaining consistency across applications.
You use Jimu to create custom:
- Widgets that extend the functionality of Experience Builder
- Extensions that enhance the capabilities of Experience Builder
- Themes that can be applied to your experiences
- Configuration that allows you to customize the behavior and appearance of your experiences
Jimu architecture
Jimu follows a modular architecture pattern with these key principles:
Modular design
Jimu follows a modular architecture where each package has a focused responsibility. This design allows developers to use only the packages they need while maintaining consistency across applications.
| Package | Description |
|---|---|
| jimu-core | Utilities, state management, data sources, and base classes |
| jimu-ui | User interface components and theming |
| jimu-arcgis | ArcGIS functionality and map integration |
| jimu-for-builder | Builder components and utilities |
| jimu-theme | Theme management and styles |
| jimu-layouts | Layout management and components |
| jimu-for-test | Testing utilities |
| jimu-icons | Icon management and SVGs |
Extensibility pattern
Jimu uses a consistent pattern for extending functionality. Below is an example of creating a custom widget:
// Example: Creating a custom widget
import { type AllWidgetProps } from 'jimu-core'
const Widget = (props: AllWidgetProps<any>) => {
return (
<div className="my-widget jimu-widget ">
<p>My Widget</p>
</div>
)
}
export default WidgetMessage action and data action are JavaScript classes, and the recommended way to create them is to extend the Abstract and Abstract classes.
export default class MyDataAction extends AbstractDataAction {}
export default class MyMessageAction extends AbstractMessageAction {}Extensions are JavaScript classes, but different extensions have different interfaces. The recommended way is to implement the interface.
export default class MyExtension implements extensionSpec.SomeExtensionInterface {}Configuration-driven
Jimu applications are configuration-driven allowing runtime customization. Below is an example of a configuration file for a widget.
{
"widgets": {
"my-widget": {
"label": "My Widget",
"uri": "widgets/my-widget",
"config": {
"title": "Custom Title"
}
}
}
}Core packages
Jimu is organized into packages where each package serves development needs. Below is an overview of each package:
jimu-core
The jimu-core package provides utilities and base classes for building experiences.
| Component | Description |
|---|---|
| Base classes and interfaces | Such as Message, Message, Data, Base, Data |
| Managers | Such as Widget, Message, Data, Extension, Data, Session |
| Redux actions | Such as app to manage state |
| Utility functions | Such as utils, url, portal, app, data, geometry |
| Components | Such as Data, Expression |
| Module loaders | moduleLoader, loadArcGISJSAPIModules |
jimu-ui
The jimu-ui package provides all UI components for building experiences. It uses emotion-js for styling and organizes components into index, basic, and advanced categories.
| Component | Description |
|---|---|
| Index | Commonly used UI components such as Button, Text, Modal |
| Basic | Components that provide focused functionality such as Date, Color |
| Advanced | Complex components that are specific to Experience Builder such as Expression, Data |
The recommended way to create UI components is to use styled components. You can find examples of styled components in the jimu-ui package documentation.
Below is an example of using the UI components:
import { Button, TextInput, Modal } from 'jimu-ui';
import { css } from 'jimu-core';
import { React } from 'jimu-core';
// Example: Using UI components with theming
const widgetStyle = css`
background: var(--background-color);
color: var(--text-color);
padding: 1rem;
`;
export default function MyWidget() {
const [isModalOpen, setIsModalOpen] = React.useState(false);
return (
<div css={widgetStyle}>
<TextInput placeholder="Enter data..." />
<Button type="primary" onClick={() => setIsModalOpen(true)}>Submit</Button>
<Modal isOpen={isModalOpen}>
<div>Modal content</div>
</Modal>
</div>
);
}jimu-arcgis
The jimu-arcgis package contains components, interfaces, classes, properties and methods for working with the ArcGIS Maps SDK for JavaScript.
Below is a list of some of the classes and components in the jimu-arcgis package.
| Component | Description |
|---|---|
| JimuMapView | Class for accessing MapView or SceneView instances |
| JimuMapViewComponent | Component for map widget and view integration |
| JimuLayerView | Class for working with layer views |
| JimuLayerViewComponent | Component for layer view operations |
Below is an example of using the map components:
import { JimuMapViewComponent } from 'jimu-arcgis';
// Example: Map integration
<JimuMapViewComponent
useMapWidgetId={useMapWidgetId}
onActiveViewChange={(jmv) => {
// Handle map view changes
jmv.view.when(() => {
console.log('Map view ready');
});
}}
/>jimu-for-builder
The jimu-for-builder package is designed to support developing the widget setting page. The setting page is the section of the builder where the experience author adds widget configuration.
The main components in this package are: builder, app, and Widget. The package extends the Redux store to add builder state, such as app and builder.
jimu-theme
The jimu-theme package provides theme management and styles for building experiences. This package provides:
styledmethod for creating styled componentswithmethod for wrapping a component with aTheme themeprop via a higher order componentusecomponent function which is a hook for accessingTheme theme- Theme-related interfaces such as
ThemeandVariable ThemeOptions
| Component | Description |
|---|---|
| styled | The method for creating styled components |
| useTheme | The react hook for accessing theme variables |
| withTheme | The method for wrapping a component with a theme prop via a higher order component |
| ThemeVariable | The interface of theme variables. |
| RawThemeOptions | The interface of theme options in json( variables.json in theme folder). |
jimu-layouts
The jimu-layouts package provides layout management and components. It includes two entries:
layout-runtime(lightweight entry) for when the app is opened at runtime. It exports theselectorswhich provides the layout tree search functionality. It also exports theLayoutcomponent for widget layout capabilities.Entry layout-builder(entry) for when the app is opened in the builder.searchfor layout tree operationsUtils
jimu-data-sources
The jimu-data-sources package provides the implementation of data sources. For most use cases, you should use the Data exported from jimu-core rather than accessing this package directly.
jimu-for-test
The jimu-for-test package provides testing utilities. Utility functions include wrap, with, with, with, mock, mock, init, get, and widget.
Below is an example of using the testing utilities to test a checkbox component.
import * as React from 'react'
import { render } from '@testing-library/react'
import { Checkbox } from 'jimu-ui'
describe('checkbox component test', () => {
it('test mount', () => {
const { container } = render(<Checkbox />)
expect(container.firstChild).not.toBeNull()
})
it('test default jimu class name', () => {
const { container } = render(<Checkbox />)
expect(container.querySelector('.jimu-checkbox')).toBeTruthy()
})
it('test "checked" attribute', () => {
const { container } = render(<Checkbox checked />)
expect(container.querySelector('.jimu-checked')).toBeTruthy()
})
it('test "indeterminate" attribute', () => {
const { container } = render(<Checkbox indeterminate />)
expect(container.querySelector('.indeterminate')).toBeTruthy()
})
it('test "disabled" attribute', () => {
const { container } = render(<Checkbox disabled />)
expect(container.querySelector('.jimu-disabled')).toBeTruthy()
})
})Another example of using the testing utilities to test a button component. You can find this example in <EX.
import { React, Immutable, getAppStore, appActions } from 'jimu-core'
import ButtonWidget from '../src/runtime/widget'
import { wrapWidget, widgetRender, getInitState, getDefaultAppConfig } from 'jimu-for-test'
import '@testing-library/jest-dom'
getAppStore().dispatch(appActions.updateStoreState(getInitState().merge({ appConfig: getDefaultAppConfig() })))
const render = widgetRender(false)
const Widget = wrapWidget(ButtonWidget)
describe('button widget test', function () {
describe('default config', function () {
const config = Immutable({
functionConfig: {
text: 'Please configure link',
toolTip: '',
linkParam: {
}
},
styleConfig: {
themeStyle: {
quickStyleType: 'default'
}
}
})
it('button widget should be render', () => {
const { queryBySelector } = render(<Widget config={config}/>)
expect(queryBySelector('.widget-button-link')).not.toBeNull()
})
})
describe('test value config', function () {
const config = Immutable({
functionConfig: {
text: 'textTest',
toolTip: 'testToolTip',
linkParam: {
value: 'detail-page',
linkType: 'PAGE'
}
},
styleConfig: {
themeStyle: {
quickStyleType: 'default'
}
}
})
it('button widget should be render', () => {
const renderResult = render(<Widget config={config}/>)
expect(renderResult.queryBySelector('.widget-button-link')).toBeInTheDocument()
expect(renderResult.queryByText('textTest')).toBeInTheDocument() //text ok
expect(renderResult.queryByTitle('testToolTip')).toBeInTheDocument() // toolTip ok
// renderResult.queryBySelector('.widget-button-link').getAttribute('href'); // link ok
})
})
})