다음을 통해 공유


원격 모니터링 솔루션 가속기 웹 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를 로컬로 실행합니다. 예제 페이지로 이동하여 서비스에서 그리드 표시 데이터를 확인합니다.

행 선택

사용자가 그리드에서 행을 선택할 수 있는 두 가지 옵션이 있습니다.

행 하드 선택

사용자가 동시에 여러 행에서 작동해야 하는 경우 행에 있는 확인란을 사용합니다.

  1. checkboxColumn을 그리드에 제공된 columnDefs에 추가하여 행의 하드 선택을 사용하도록 설정합니다. checkboxColumn/src/components/shared/pcsGrid/pcsGrid.js에 정의됩니다.

    this.columnDefs = [
      checkboxColumn,
      exampleColumnDefs.id,
      exampleColumnDefs.description
    ];
    
  2. 선택한 항목에 액세스하려면 내부 그리드 API에 대한 참조를 가져옵니다.

    onGridReady = gridReadyEvent => {
      this.gridApi = gridReadyEvent.api;
      // Call the onReady props if it exists
      if (isFunc(this.props.onGridReady)) {
        this.props.onGridReady(gridReadyEvent);
      }
    };
    
  3. 그리드의 행이 하드 선택된 경우 페이지에 컨텍스트 단추를 제공합니다.

    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);
      }
      //...
    }
    
  4. 컨텍스트 단추를 클릭하면 작업을 수행할 수 있도록 하드 선택된 항목을 가져옵니다.

    doSomething = () => {
      //Just for demo purposes. Don't console log in a real grid.
      console.log('Hard selected rows', this.gridApi.getSelectedRows());
    };
    

행 소프트 선택

사용자가 단일 행에서만 작동해야 하는 경우 columnDefs에서 하나 이상의 열에 대해 소프트 선택 링크를 구성합니다.

  1. exampleGridConfig.js에서 SoftSelectLinkRenderercolumnDef에 대해 cellRendererFramework로 추가합니다.

    export const exampleColumnDefs = {
      id: {
        headerName: 'examples.grid.name',
        field: 'id',
        sort: 'asc',
        cellRendererFramework: SoftSelectLinkRenderer
      }
    };
    
  2. 소프트 선택 링크를 클릭하면 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에 추가하는 것입니다.

원격 모니터링 솔루션 가속기에 대한 자세한 개념 정보는 원격 모니터링 아키텍처를 참조하세요.