You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devlake.apache.org by li...@apache.org on 2022/11/16 06:53:05 UTC
[incubator-devlake] branch main updated: feat(config-ui): jira board select to support miller-columns component (#3744)
This is an automated email from the ASF dual-hosted git repository.
likyh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git
The following commit(s) were added to refs/heads/main by this push:
new 9909219ac feat(config-ui): jira board select to support miller-columns component (#3744)
9909219ac is described below
commit 9909219ac643536b420e8b0e65143ae9c1b52d32
Author: 青湛 <0x...@gmail.com>
AuthorDate: Wed Nov 16 14:53:02 2022 +0800
feat(config-ui): jira board select to support miller-columns component (#3744)
* refactor(config-ui): move file components/gitlab/request to components/utils/request
* fix(config-ui): selectedItems not required in miller-columns component
* feat(config-ui): support scroll load on miller-columns component
* feat(config-ui): jira board select to support miller-columns component
---
.../blueprints/create-workflow/DataScopes.jsx | 35 +++++++----
.../src/components/gitlab/miller-columns/index.tsx | 40 +-----------
.../miller-columns/use-gitlab-miller-columns.ts | 2 +-
.../use-gitlab-project-selector.ts | 3 +-
.../miller-columns/styled.ts => jira/config.ts} | 7 +--
.../miller-columns/styled.ts => jira/index.ts} | 6 +-
.../src/components/jira/miller-columns/index.tsx | 61 +++++++++++++++++++
.../jira/miller-columns/use-jira-miller-columns.ts | 71 ++++++++++++++++++++++
.../miller-columns/components/column/column.tsx | 57 +++++++++++++++--
.../miller-columns/components/column/styled.ts | 4 ++
.../miller-columns/hooks/use-miller-columns.ts | 2 +-
.../components/miller-columns/miller-columns.tsx | 48 +++++++--------
.../src/components/{gitlab => utils}/request.ts | 0
13 files changed, 242 insertions(+), 94 deletions(-)
diff --git a/config-ui/src/components/blueprints/create-workflow/DataScopes.jsx b/config-ui/src/components/blueprints/create-workflow/DataScopes.jsx
index 9b3a3593c..6ee5d3124 100644
--- a/config-ui/src/components/blueprints/create-workflow/DataScopes.jsx
+++ b/config-ui/src/components/blueprints/create-workflow/DataScopes.jsx
@@ -32,6 +32,8 @@ import DataDomainsSelector from '@/components/blueprints/DataDomainsSelector'
import NoData from '@/components/NoData'
import { GitLabMillerColumns, GitLabProjectSelector } from '@/components/gitlab'
import GitlabProject from '@/models/GitlabProject'
+import { JIRAMillerColumns } from '@/components/jira'
+import JiraBoard from '@/models/JiraBoard'
import GitHubProject from '@/models/GithubProject'
import JenkinsJobsSelector from '@/components/blueprints/JenkinsJobsSelector'
@@ -190,17 +192,28 @@ const DataScopes = (props) => {
<>
<h4>Boards *</h4>
<p>Select the boards you would like to sync.</p>
- <BoardsSelector
- items={jiraBoards}
- selectedItems={selectedScopeEntities}
- onQueryChange={setBoardSearch}
- onItemSelect={setScopeEntities}
- onClear={setScopeEntities}
- onRemove={setScopeEntities}
- disabled={isSaving}
- configuredConnection={configuredConnection}
- isLoading={isFetching}
- />
+ {!activeStep ? (
+ <BoardsSelector
+ items={jiraBoards}
+ selectedItems={selectedScopeEntities}
+ onQueryChange={setBoardSearch}
+ onItemSelect={setScopeEntities}
+ onClear={setScopeEntities}
+ onRemove={setScopeEntities}
+ disabled={isSaving}
+ configuredConnection={configuredConnection}
+ isLoading={isFetching}
+ />
+ ) : (
+ <JIRAMillerColumns
+ connectionId={configuredConnection.connectionId}
+ onChangeItems={(items) =>
+ setScopeEntities(
+ items.map((it) => new JiraBoard(it))
+ )
+ }
+ />
+ )}
</>
)}
diff --git a/config-ui/src/components/gitlab/miller-columns/index.tsx b/config-ui/src/components/gitlab/miller-columns/index.tsx
index 648d4602a..5db575c50 100644
--- a/config-ui/src/components/gitlab/miller-columns/index.tsx
+++ b/config-ui/src/components/gitlab/miller-columns/index.tsx
@@ -18,18 +18,13 @@
import React, { useEffect, useState } from 'react'
-import type { ColumnType, ItemType } from '@/components/miller-columns'
-import {
- MillerColumns,
- ItemStatusEnum,
- ItemTypeEnum
-} from '@/components/miller-columns'
+import type { ItemType } from '@/components/miller-columns'
+import { MillerColumns, ItemTypeEnum } from '@/components/miller-columns'
import {
useGitLabMillerColumns,
UseGitLabMillerColumnsProps
} from './use-gitlab-miller-columns'
-import * as S from './styled'
interface Props extends UseGitLabMillerColumnsProps {
disabledItemIds?: Array<number>
@@ -63,21 +58,6 @@ export const GitLabMillerColumns = ({
onChangeItems(curItems)
}, [seletedIds])
- const renderColumnBottom = ({
- isLoading,
- isEmpty
- }: {
- isLoading: boolean
- isEmpty: boolean
- }) => {
- switch (true) {
- case isLoading:
- return <S.Placeholder>Loading...</S.Placeholder>
- case isEmpty:
- return <S.Placeholder>No Data.</S.Placeholder>
- }
- }
-
return (
<MillerColumns
height={300}
@@ -87,22 +67,6 @@ export const GitLabMillerColumns = ({
selectedItemIds={seletedIds}
onSelectedItemIds={setSelectedIds}
onExpandItem={onExpandItem}
- renderColumnBottom={(col: ColumnType) => {
- if (!col.parentId) {
- return renderColumnBottom({
- isLoading: !itemTree.root,
- isEmpty: !itemTree.root || !itemTree.root.items.length
- })
- } else {
- return renderColumnBottom({
- isLoading:
- !itemTree[col.parentId] ||
- itemTree[col.parentId].status === ItemStatusEnum.PENDING,
- isEmpty:
- !itemTree[col.parentId] || !itemTree[col.parentId].items.length
- })
- }
- }}
/>
)
}
diff --git a/config-ui/src/components/gitlab/miller-columns/use-gitlab-miller-columns.ts b/config-ui/src/components/gitlab/miller-columns/use-gitlab-miller-columns.ts
index 1874f73c1..ea8c1dabd 100644
--- a/config-ui/src/components/gitlab/miller-columns/use-gitlab-miller-columns.ts
+++ b/config-ui/src/components/gitlab/miller-columns/use-gitlab-miller-columns.ts
@@ -20,8 +20,8 @@ import { useMemo, useCallback } from 'react'
import type { ItemType } from '@/components/miller-columns'
import { useLoadItems, ItemTypeEnum } from '@/components/miller-columns'
+import request from '@/components/utils/request'
-import request from '../request'
import { getGitLabProxyApiPrefix } from '../config'
export interface UseGitLabMillerColumnsProps {
diff --git a/config-ui/src/components/gitlab/project-selector/use-gitlab-project-selector.ts b/config-ui/src/components/gitlab/project-selector/use-gitlab-project-selector.ts
index 66491d534..527ccd367 100644
--- a/config-ui/src/components/gitlab/project-selector/use-gitlab-project-selector.ts
+++ b/config-ui/src/components/gitlab/project-selector/use-gitlab-project-selector.ts
@@ -18,7 +18,8 @@
import { useState, useEffect, useMemo } from 'react'
-import request from '../request'
+import request from '@/components/utils/request'
+
import { getGitLabProxyApiPrefix } from '../config'
export type ItemType = {
diff --git a/config-ui/src/components/gitlab/miller-columns/styled.ts b/config-ui/src/components/jira/config.ts
similarity index 87%
copy from config-ui/src/components/gitlab/miller-columns/styled.ts
copy to config-ui/src/components/jira/config.ts
index 6b7cbc9e7..efca6cf86 100644
--- a/config-ui/src/components/gitlab/miller-columns/styled.ts
+++ b/config-ui/src/components/jira/config.ts
@@ -16,8 +16,5 @@
*
*/
-import styled from '@emotion/styled'
-
-export const Placeholder = styled.div`
- padding: 4px 12px;
-`
+export const getJIRAApiPrefix = (connectionId: string) =>
+ `/plugins/jira/connections/${connectionId}/proxy/rest`
diff --git a/config-ui/src/components/gitlab/miller-columns/styled.ts b/config-ui/src/components/jira/index.ts
similarity index 88%
rename from config-ui/src/components/gitlab/miller-columns/styled.ts
rename to config-ui/src/components/jira/index.ts
index 6b7cbc9e7..1c06557ce 100644
--- a/config-ui/src/components/gitlab/miller-columns/styled.ts
+++ b/config-ui/src/components/jira/index.ts
@@ -16,8 +16,4 @@
*
*/
-import styled from '@emotion/styled'
-
-export const Placeholder = styled.div`
- padding: 4px 12px;
-`
+export * from './miller-columns'
diff --git a/config-ui/src/components/jira/miller-columns/index.tsx b/config-ui/src/components/jira/miller-columns/index.tsx
new file mode 100644
index 000000000..dc40bc47e
--- /dev/null
+++ b/config-ui/src/components/jira/miller-columns/index.tsx
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import React, { useState, useEffect } from 'react'
+
+import type { ItemType } from '@/components/miller-columns'
+import { MillerColumns } from '@/components/miller-columns'
+
+import {
+ useJIRAMillerColumns,
+ UseJIRAMillerColumnsProps
+} from './use-jira-miller-columns'
+
+interface Props extends UseJIRAMillerColumnsProps {
+ onChangeItems: (items: Array<Pick<ItemType, 'id' | 'title'>>) => void
+}
+
+export const JIRAMillerColumns = ({ connectionId, onChangeItems }: Props) => {
+ const [seletedIds, setSelectedIds] = useState<Array<ItemType['id']>>([])
+
+ const { items, hasMore, onScroll } = useJIRAMillerColumns({ connectionId })
+
+ useEffect(() => {
+ onChangeItems(
+ items
+ .filter((it) => seletedIds.includes(it.id))
+ .map((it) => ({
+ id: it.id,
+ title: it.title
+ }))
+ )
+ }, [seletedIds])
+
+ return (
+ <MillerColumns
+ height={300}
+ items={items}
+ selectedItemIds={seletedIds}
+ onSelectedItemIds={setSelectedIds}
+ scrollProps={{
+ hasMore,
+ onScroll
+ }}
+ />
+ )
+}
diff --git a/config-ui/src/components/jira/miller-columns/use-jira-miller-columns.ts b/config-ui/src/components/jira/miller-columns/use-jira-miller-columns.ts
new file mode 100644
index 000000000..212f3d60d
--- /dev/null
+++ b/config-ui/src/components/jira/miller-columns/use-jira-miller-columns.ts
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { useState, useEffect, useMemo } from 'react'
+
+import type { ItemType } from '@/components/miller-columns'
+import { ItemTypeEnum, ItemStatusEnum } from '@/components/miller-columns'
+
+import request from '@/components/utils/request'
+
+import { getJIRAApiPrefix } from '../config'
+
+export interface UseJIRAMillerColumnsProps {
+ connectionId: string
+}
+
+export const useJIRAMillerColumns = ({
+ connectionId
+}: UseJIRAMillerColumnsProps) => {
+ const [items, setItems] = useState<Array<ItemType>>([])
+ const [hasMore, setHasMore] = useState(true)
+ const [page, setPage] = useState(1)
+ const [pageSize] = useState(50)
+
+ const prefix = useMemo(() => getJIRAApiPrefix(connectionId), [connectionId])
+
+ const updateItems = (arr: Array<{ id: number; name: string }>) =>
+ arr.map((it) => ({
+ id: it.id,
+ title: it.name,
+ type: ItemTypeEnum.LEAF,
+ status: ItemStatusEnum.READY,
+ items: []
+ }))
+
+ useEffect(() => {
+ ;(async () => {
+ const res = await request(`${prefix}/agile/1.0/board`, {
+ data: { startAt: (page - 1) * pageSize, maxResults: pageSize }
+ })
+ setHasMore(!res.isLast)
+ setItems([...items, ...updateItems(res.values)])
+ })()
+ }, [prefix, page, pageSize])
+
+ return useMemo(
+ () => ({
+ items,
+ hasMore,
+ onScroll() {
+ setPage(page + 1)
+ }
+ }),
+ [items, hasMore]
+ )
+}
diff --git a/config-ui/src/components/miller-columns/components/column/column.tsx b/config-ui/src/components/miller-columns/components/column/column.tsx
index 967cf005d..269fc2328 100644
--- a/config-ui/src/components/miller-columns/components/column/column.tsx
+++ b/config-ui/src/components/miller-columns/components/column/column.tsx
@@ -16,26 +16,71 @@
*
*/
-import React from 'react'
+import React, { useState, useEffect, useCallback } from 'react'
+import InfiniteScroll from 'react-infinite-scroll-component'
import type { ItemType } from '../../types'
import * as S from './styled'
-interface Props {
+export interface ColumnsProps {
items: Array<ItemType>
renderItem: (item: ItemType) => React.ReactNode
height?: number
title?: string | React.ReactNode
bottom?: React.ReactNode
+ scrollProps?: {
+ hasMore: boolean
+ onScroll: () => void
+ renderLoader?: () => React.ReactNode
+ renderBottom?: () => React.ReactNode
+ }
}
-export const Column = ({ items, renderItem, height, title, bottom }: Props) => {
+export const Column = ({
+ items,
+ renderItem,
+ height,
+ title,
+ scrollProps
+}: ColumnsProps) => {
+ const [hasMore, setHasMore] = useState(true)
+
+ useEffect(() => {
+ if (scrollProps) {
+ setHasMore(scrollProps.hasMore)
+ }
+ }, [scrollProps])
+
+ const handleNext = useCallback(() => {
+ if (scrollProps) {
+ scrollProps.onScroll()
+ } else {
+ setHasMore(false)
+ }
+ }, [scrollProps])
+
+ const loader = scrollProps?.renderLoader?.() ?? (
+ <S.StatusWrapper>Loading...</S.StatusWrapper>
+ )
+
+ const bottom = scrollProps?.renderBottom?.() ?? (
+ <S.StatusWrapper>All Data Loaded.</S.StatusWrapper>
+ )
+
return (
- <S.Container height={height}>
+ <S.Container id='miller-columns-column-container' height={height}>
{title && <div className='title'>{title}</div>}
- {items.map((it) => renderItem(it))}
- {bottom}
+ <InfiniteScroll
+ dataLength={items.length}
+ hasMore={hasMore}
+ next={handleNext}
+ loader={loader}
+ scrollableTarget='miller-columns-column-container'
+ endMessage={bottom}
+ >
+ {items.map((it) => renderItem(it))}
+ </InfiniteScroll>
</S.Container>
)
}
diff --git a/config-ui/src/components/miller-columns/components/column/styled.ts b/config-ui/src/components/miller-columns/components/column/styled.ts
index b91d76491..e522a0693 100644
--- a/config-ui/src/components/miller-columns/components/column/styled.ts
+++ b/config-ui/src/components/miller-columns/components/column/styled.ts
@@ -38,3 +38,7 @@ export const Container = styled.div<{ height?: number }>`
color: #292b3f;
}
`
+
+export const StatusWrapper = styled.div`
+ padding: 4px 12px;
+`
diff --git a/config-ui/src/components/miller-columns/hooks/use-miller-columns.ts b/config-ui/src/components/miller-columns/hooks/use-miller-columns.ts
index bc57cf79c..8f0f6b8fb 100644
--- a/config-ui/src/components/miller-columns/hooks/use-miller-columns.ts
+++ b/config-ui/src/components/miller-columns/hooks/use-miller-columns.ts
@@ -30,7 +30,7 @@ export interface UseMillerColumnsProps {
activeItemId?: ItemType['id']
onActiveItemId?: (id: ItemType['id']) => void
disabledItemIds?: Array<ItemType['id']>
- selectedItemIds: Array<ItemType['id']>
+ selectedItemIds?: Array<ItemType['id']>
onSelectedItemIds?: (ids: Array<ItemType['id']>) => void
onExpandItem?: (item: ItemType) => void
}
diff --git a/config-ui/src/components/miller-columns/miller-columns.tsx b/config-ui/src/components/miller-columns/miller-columns.tsx
index 7aadb9a95..b07f2d621 100644
--- a/config-ui/src/components/miller-columns/miller-columns.tsx
+++ b/config-ui/src/components/miller-columns/miller-columns.tsx
@@ -19,21 +19,20 @@
import React from 'react'
import { useMillerColumns, UseMillerColumnsProps } from './hooks'
-import { Column, Item } from './components'
+import { Column, ColumnsProps, Item } from './components'
-import { ColumnType } from './types'
import * as S from './styled'
interface Props extends UseMillerColumnsProps {
height?: number
firstColumnTitle?: React.ReactNode
- renderColumnBottom?: (col: ColumnType) => React.ReactNode
+ scrollProps?: ColumnsProps['scrollProps']
}
export const MillerColumns = ({
firstColumnTitle,
height,
- renderColumnBottom,
+ scrollProps,
...props
}: Props) => {
const { columns, getStatus, getChekecdStatus, onExpandItem, onSelectItem } =
@@ -41,28 +40,25 @@ export const MillerColumns = ({
return (
<S.Container>
- {columns.map((col, i) => {
- const bottom = renderColumnBottom?.(col)
- return (
- <Column
- key={col.parentId}
- items={col.items}
- renderItem={(item) => (
- <Item
- key={item.id}
- item={item}
- status={getStatus(item, col)}
- checkStatus={getChekecdStatus(item)}
- onExpandItem={onExpandItem}
- onSelectItem={onSelectItem}
- />
- )}
- height={height}
- title={i === 0 && firstColumnTitle}
- bottom={bottom}
- />
- )
- })}
+ {columns.map((col, i) => (
+ <Column
+ key={col.parentId}
+ items={col.items}
+ renderItem={(item) => (
+ <Item
+ key={item.id}
+ item={item}
+ status={getStatus(item, col)}
+ checkStatus={getChekecdStatus(item)}
+ onExpandItem={onExpandItem}
+ onSelectItem={onSelectItem}
+ />
+ )}
+ height={height}
+ title={i === 0 && firstColumnTitle}
+ scrollProps={scrollProps}
+ />
+ ))}
</S.Container>
)
}
diff --git a/config-ui/src/components/gitlab/request.ts b/config-ui/src/components/utils/request.ts
similarity index 100%
rename from config-ui/src/components/gitlab/request.ts
rename to config-ui/src/components/utils/request.ts