원격 모니터링 솔루션 가속기 웹 UI에 사용자 지정 그리드 추가
이 문서에서는 원격 모니터링 솔루션 가속기 웹 UI의 페이지에 새 그리드를 추가하는 방법을 보여줍니다. 문서에서는 다음을 설명합니다.
- 로컬 개발 환경을 준비하는 방법.
- 웹 UI의 페이지에 새 그리드를 추가하는 방법.
이 문서의 예제 그리드는 원격 모니터링 솔루션 가속기 웹 UI에 사용자 지정 서비스 추가 방법 문서에서 추가 방법을 보여주는 서비스의 데이터를 표시합니다.
사전 요구 사항
이 방법 가이드의 단계를 완료하려면 로컬 개발 머신에 다음과 같은 소프트웨어가 설치되어 있어야 합니다.
시작하기 전에
계속하려면 다음 문서의 단계를 완료해야 합니다.
그리드 추가
웹 UI에 그리드를 추가하려면 그리드를 정의하는 원본 파일을 추가하고, 웹 UI가 새 구성 요소를 인식하도록 일부 기존 파일을 수정해야 합니다.
그리드를 정의하는 새 파일 추가
시작할 수 있도록 src/walkthrough/components/pages/pageWithGrid/exampleGrid 폴더에 그리드를 정의하는 파일이 포함되어 있습니다.
exampleGrid.js
import React, { Component } from 'react';
import { Btn, ComponentArray, PcsGrid } from 'components/shared';
import { exampleColumnDefs, defaultExampleGridProps } from './exampleGridConfig';
import { isFunc, svgs, translateColumnDefs } from 'utilities';
import { checkboxColumn } from 'components/shared/pcsGrid/pcsGridConfig';
const initialState = {
softSelectedId: undefined
};
/**
* A grid for displaying example data
*
* Encapsulates the PcsGrid props
*/
export class ExampleGrid extends Component {
constructor(props) {
super(props);
// Set the initial state
this.state = initialState;
// Default device grid columns
this.columnDefs = [
checkboxColumn,
exampleColumnDefs.id,
exampleColumnDefs.description
];
// Set up the available context buttons.
// If these are subject to user permissions, use the Protected component (src/components/shared/protected).
this.contextBtns =
<ComponentArray>
<Btn svg={svgs.reconfigure} onClick={this.clickContextBtn('btn1')}>{props.t('walkthrough.pageWithGrid.grid.btn1')}</Btn>
<Btn svg={svgs.trash} onClick={this.clickContextBtn('btn2')}>{props.t('walkthrough.pageWithGrid.grid.btn2')}</Btn>
</ComponentArray>;
}
/**
* Get the grid api options
*
* @param {Object} gridReadyEvent An object containing access to the grid APIs
*/
onGridReady = gridReadyEvent => {
this.gridApi = gridReadyEvent.api;
// Call the onReady props if it exists
if (isFunc(this.props.onGridReady)) {
this.props.onGridReady(gridReadyEvent);
}
};
clickContextBtn = (input) => () => {
//Just for demo purposes. Don't console log in a real grid.
console.log('Context button clicked', input);
console.log('Hard selected rows', this.gridApi.getSelectedRows());
};
/**
* Handles soft select props method.
* Soft selection happens when the user clicks on the row.
*
* @param rowId The id of the currently soft selected item
* @param rowData The rowData from the underlying grid. MAY BE OUT OF DATE.
*/
onSoftSelectChange = (rowId, rowData) => {
//Note: only the Id is reliable, rowData may be out of date
const { onSoftSelectChange } = this.props;
if (rowId) {
console.log('Soft selected', rowId); //Just for demo purposes. Don't console log a real grid.
this.setState({ softSelectedId: rowId });
}
if (isFunc(onSoftSelectChange)) {
onSoftSelectChange(rowId, rowData);
}
}
/**
* Handles context filter changes and calls any hard select props method.
* Hard selection happens when the user checks the box for the row.
*
* @param {Array} selectedObjs A list of currently selected objects in the grid
*/
onHardSelectChange = (selectedObjs) => {
const { onContextMenuChange, onHardSelectChange } = this.props;
// Show the context buttons when there are rows checked.
if (isFunc(onContextMenuChange)) {
onContextMenuChange(selectedObjs.length > 0 ? this.contextBtns : null);
}
if (isFunc(onHardSelectChange)) {
onHardSelectChange(selectedObjs);
}
}
getSoftSelectId = ({ id } = '') => id;
render() {
const gridProps = {
/* Grid Properties */
...defaultExampleGridProps,
columnDefs: translateColumnDefs(this.props.t, this.columnDefs),
sizeColumnsToFit: true,
getSoftSelectId: this.getSoftSelectId,
softSelectId: (this.state.softSelectedDevice || {}).id,
...this.props, // Allow default property overrides
deltaRowDataMode: true,
enableSorting: true,
unSortIcon: true,
getRowNodeId: ({ id }) => id,
context: {
t: this.props.t
},
/* Grid Events */
onRowClicked: ({ node }) => node.setSelected(!node.isSelected()),
onGridReady: this.onGridReady,
onSoftSelectChange: this.onSoftSelectChange,
onHardSelectChange: this.onHardSelectChange
};
return (
<PcsGrid key="example-grid-key" {...gridProps} />
);
}
}
exampleGridConfig.js
import Config from 'app.config';
import { SoftSelectLinkRenderer } from 'components/shared/cellRenderers';
/** A collection of column definitions for the example grid */
export const exampleColumnDefs = {
id: {
headerName: 'walkthrough.pageWithGrid.grid.name',
field: 'id',
sort: 'asc',
cellRendererFramework: SoftSelectLinkRenderer
},
description: {
headerName: 'walkthrough.pageWithGrid.grid.description',
field: 'descr'
}
};
/** Given an example object, extract and return the device Id */
export const getSoftSelectId = ({ id }) => id;
/** Shared example grid AgGrid properties */
export const defaultExampleGridProps = {
enableColResize: true,
multiSelect: true,
pagination: false,
paginationPageSize: Config.paginationPageSize,
rowSelection: 'multiple'
};
src/walkthrough/components/pages/pageWithGrid/exampleGrid 폴더를 src/components/pages/example 폴더에 복사합니다.
페이지에 그리드 추가
src/components/pages/example/basicPage.container.js를 다음과 같이 수정하여 서비스 정의를 가져옵니다.
import { connect } from 'react-redux';
import { translate } from 'react-i18next';
import {
epics as exampleEpics,
getExamples,
getExamplesError,
getExamplesLastUpdated,
getExamplesPendingStatus
} from 'store/reducers/exampleReducer';
import { BasicPage } from './basicPage';
// Pass the data
const mapStateToProps = state => ({
data: getExamples(state),
error: getExamplesError(state),
isPending: getExamplesPendingStatus(state),
lastUpdated: getExamplesLastUpdated(state)
});
// Wrap the dispatch method
const mapDispatchToProps = dispatch => ({
fetchData: () => dispatch(exampleEpics.actions.fetchExamples())
});
export const BasicPageContainer = translate()(connect(mapStateToProps, mapDispatchToProps)(BasicPage));
src/components/pages/example/basicPage.js를 다음과 같이 수정하여 그리드를 추가합니다.
// Copyright (c) Microsoft. All rights reserved.
import React, { Component } from 'react';
import {
AjaxError,
ContextMenu,
PageContent,
RefreshBar
} from 'components/shared';
import { ExampleGrid } from './exampleGrid';
import './basicPage.css';
export class BasicPage extends Component {
constructor(props) {
super(props);
this.state = { contextBtns: null };
}
componentDidMount() {
const { isPending, lastUpdated, fetchData } = this.props;
if (!lastUpdated && !isPending) fetchData();
}
onGridReady = gridReadyEvent => this.gridApi = gridReadyEvent.api;
onContextMenuChange = contextBtns => this.setState({ contextBtns });
render() {
const { t, data, error, isPending, lastUpdated, fetchData } = this.props;
const gridProps = {
onGridReady: this.onGridReady,
rowData: isPending ? undefined : data || [],
onContextMenuChange: this.onContextMenuChange,
t: this.props.t
};
return [
<ContextMenu key="context-menu">
{this.state.contextBtns}
</ContextMenu>,
<PageContent className="basic-page-container" key="page-content">
<RefreshBar refresh={fetchData} time={lastUpdated} isPending={isPending} t={t} />
{!!error && <AjaxError t={t} error={error} />}
{!error && <ExampleGrid {...gridProps} />}
</PageContent>
];
}
}
src/components/pages/example/basicPage.test.js를 다음과 같이 수정하여 테스트를 업데이트합니다.
// Copyright (c) Microsoft. All rights reserved.
import React from 'react';
import { shallow } from 'enzyme';
import 'polyfills';
import { BasicPage } from './basicPage';
describe('BasicPage Component', () => {
it('Renders without crashing', () => {
const fakeProps = {
data: undefined,
error: undefined,
isPending: false,
lastUpdated: undefined,
fetchData: () => { },
t: () => { },
};
const wrapper = shallow(
<BasicPage {...fakeProps} />
);
});
});
그리드 테스트
아직 로컬에서 웹 UI가 실행되고 있지 않으면 리포지토리의 로컬 복사본 루트에서 다음 명령을 실행합니다.
npm start
이전 명령은 https://localhost:3000/dashboard
에서 UI를 로컬로 실행합니다.
예제 페이지로 이동하여 서비스에서 그리드 표시 데이터를 확인합니다.
행 선택
사용자가 그리드에서 행을 선택할 수 있는 두 가지 옵션이 있습니다.
행 하드 선택
사용자가 동시에 여러 행에서 작동해야 하는 경우 행에 있는 확인란을 사용합니다.
checkboxColumn을 그리드에 제공된 columnDefs에 추가하여 행의 하드 선택을 사용하도록 설정합니다. checkboxColumn은 /src/components/shared/pcsGrid/pcsGrid.js에 정의됩니다.
this.columnDefs = [ checkboxColumn, exampleColumnDefs.id, exampleColumnDefs.description ];
선택한 항목에 액세스하려면 내부 그리드 API에 대한 참조를 가져옵니다.
onGridReady = gridReadyEvent => { this.gridApi = gridReadyEvent.api; // Call the onReady props if it exists if (isFunc(this.props.onGridReady)) { this.props.onGridReady(gridReadyEvent); } };
그리드의 행이 하드 선택된 경우 페이지에 컨텍스트 단추를 제공합니다.
this.contextBtns = [ <Btn key="context-btn-1" svg={svgs.reconfigure} onClick={this.doSomething()}>Button 1</Btn>, <Btn key="context-btn-2" svg={svgs.trash} onClick={this.doSomethingElse()}>Button 2</Btn> ];
onHardSelectChange = (selectedObjs) => { const { onContextMenuChange, onHardSelectChange } = this.props; // Show the context buttons when there are rows checked. if (isFunc(onContextMenuChange)) { onContextMenuChange(selectedObjs.length > 0 ? this.contextBtns : null); } //... }
컨텍스트 단추를 클릭하면 작업을 수행할 수 있도록 하드 선택된 항목을 가져옵니다.
doSomething = () => { //Just for demo purposes. Don't console log in a real grid. console.log('Hard selected rows', this.gridApi.getSelectedRows()); };
행 소프트 선택
사용자가 단일 행에서만 작동해야 하는 경우 columnDefs에서 하나 이상의 열에 대해 소프트 선택 링크를 구성합니다.
exampleGridConfig.js에서 SoftSelectLinkRenderer를 columnDef에 대해 cellRendererFramework로 추가합니다.
export const exampleColumnDefs = { id: { headerName: 'examples.grid.name', field: 'id', sort: 'asc', cellRendererFramework: SoftSelectLinkRenderer } };
소프트 선택 링크를 클릭하면 onSoftSelectChange 이벤트가 트리거됩니다. 세부 정보 플라이아웃 열기와 같이 해당 행에 대해 필요한 모든 작업을 수행합니다. 이 예제에서는 간단히 콘솔에 작성합니다.
onSoftSelectChange = (rowId, rowData) => { //Note: only the Id is reliable, rowData may be out of date const { onSoftSelectChange } = this.props; if (rowId) { //Just for demo purposes. Don't console log a real grid. console.log('Soft selected', rowId); this.setState({ softSelectedId: rowId }); } if (isFunc(onSoftSelectChange)) { onSoftSelectChange(rowId, rowData); } }
다음 단계
이 문서에서는 원격 모니터링 솔루션 가속기의 웹 UI에서 페이지를 추가하거나 사용자 지정하는 데 용이하게 사용할 수 있는 리소스에 대해 알아보았습니다.
그리드를 정의했으니, 그 다음 단계는 예제 페이지에 표시되는 사용자 지정 플라이아웃을 원격 모니터링 솔루션 가속기 웹 UI에 추가하는 것입니다.
원격 모니터링 솔루션 가속기에 대한 자세한 개념 정보는 원격 모니터링 아키텍처를 참조하세요.