diff --git a/packages/studio/src/api-data.json b/packages/studio/src/api-data.json deleted file mode 100644 index 9009f1e..0000000 --- a/packages/studio/src/api-data.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "apis": [ - { - "id": "0", - "name": "Pet Store API", - "description": "A sample API that uses a pet store as an example to demonstrate features in the OpenAPI 3.0 specification.", - "createdOn": "January 29, 2020", - "createdBy": "cmolloy@redhat.com", - "tags": ["Pet store tag", "Another tag", "Tag one"], - "type": "Open API 3.0.2" - }, - { - "id": "1", - "name": "Sports Store API", - "description": "A sample API that uses a pet store as an example to demonstrate features in the OpenAPI 3.0 specification.", - "createdOn": "January 30, 2020", - "createdBy": "dlabaj@redhat.com", - "tags": ["Sports store tag", "Another tag", "Tag two"], - "type": "Open API 3.0.2" - }, - { - "id": "2", - "name": "Weather API", - "description": "A sample API that uses a pet store as an example to demonstrate features in the OpenAPI 3.0 specification.", - "createdOn": "January 18, 2020", - "createdBy": "cmolloy@redhat.com", - "tags": ["Weather store tag", "Another tag", "Tag three"], - "type": "Open API 3.0.2" - }, - { - "id": "3", - "name": "Clothes Store API", - "description": "A sample API that uses a pet store as an example to demonstrate features in the OpenAPI 3.0 specification.", - "createdOn": "January 24, 2020", - "createdBy": "dlabaj@redhat.com", - "tags": ["Clothes store tag", "Another tag", "Tag four"], - "type": "Open API 3.0.2" - } - ] -} diff --git a/packages/studio/src/app/app.css b/packages/studio/src/app/app.css index 8e0ac64..a8c40b5 100644 --- a/packages/studio/src/app/app.css +++ b/packages/studio/src/app/app.css @@ -29,7 +29,6 @@ height: 100vh; } - .app-api-title { color: var(--pf-global--primary-color--100); } diff --git a/packages/studio/src/app/components/api/apiCard/apiCard.tsx b/packages/studio/src/app/components/api/apiCard/apiCard.tsx deleted file mode 100644 index 30b5de9..0000000 --- a/packages/studio/src/app/components/api/apiCard/apiCard.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; -import { Card, CardHead, CardHeader, CardBody, CardFooter, CardActions } from '@patternfly/react-core'; -import {ApiTag} from '../apiTag'; -import ApicurioIcon from '../../../assets/apicurio-icon.png'; -import './apiCard.css' - -interface ApiCardProps { - id: string, - name: string, - description: string, - tags: string[], - type?: string -} - -export const ApiCard: React.FunctionComponent = ({ name, description, tags = []}: ApiCardProps) => { - return ( - - - - - - - {name} - - - {description} - - -
- {tags.map((tag, index) => - - )} -
-
-
- ); -} diff --git a/packages/studio/src/app/components/api/apiCard/apiCardView.tsx b/packages/studio/src/app/components/api/apiCard/apiCardView.tsx index 36c9bbe..6ccf24b 100644 --- a/packages/studio/src/app/components/api/apiCard/apiCardView.tsx +++ b/packages/studio/src/app/components/api/apiCard/apiCardView.tsx @@ -1,26 +1,105 @@ -import React, { useContext } from 'react'; -import { Gallery } from '@patternfly/react-core'; -import {ApiCard} from './apiCard'; -import { GlobalContext } from '../../../../context'; +import React, { useContext, useState } from "react"; +import { + Card, + CardHead, + CardHeader, + CardBody, + CardFooter, + CardActions, + Gallery, + Checkbox +} from "@patternfly/react-core"; +import { ApiTag } from "../apiTag"; +import ApicurioIcon from "./../../../assets/apicurio-icon.png"; +import "./apiCard.css"; +import AppDropdownKebab from "../apiDropDownKebab/apiDropdownKebab"; +import { GlobalContext, GlobalContextObj } from "../../../../context"; -export const ApiCardView: React.FunctionComponent = (props) => { - const { apis } = {... useContext(GlobalContext).store} +interface ApiCardProps { + id?: string; + name?: string; + description?: string; + tags?: string[]; + type?: string; +} - const cardList = apis.map((api, index) => - - ); +export const ApiCardView: React.FunctionComponent = ({ + name, + description, + tags = [] +}: ApiCardProps) => { + const globalContext: GlobalContextObj = useContext(GlobalContext); + const { selectedItems } = { ...globalContext.store.toolbarStatus }; + + const getAllItems = () => { + const collection: number[] = []; + for (const items of selectedItems) { + // tslint:disable-next-line: radix + collection.push(parseInt(items.id)); + } + return collection; + }; + + const handleCheckboxClick = (checked: boolean, e: any) => { + const { value } = e.target; + + if (checked) { + globalContext.updateToolbarStatus({ + areAllSelected: + globalContext.store.apis.length - 1 === selectedItems.length, + selectedItems: selectedItems.concat(value * 1) + }); + } else { + globalContext.updateToolbarStatus({ + areAllSelected: false, + selectedItems: selectedItems.filter(item => item !== Number(value)) + }); + } + }; + + const { apis } = { ...useContext(GlobalContext).store }; return ( - {cardList} + {apis.map((api, key) => ( + + + + + + + + + + + {api.name} + + + {api.description} + + +
+ {api.tags.map( + (tag: string, index: string | number | undefined) => ( + + ) + )} +
+
+
+
+ ))}
); -} - -export default ApiCardView; \ No newline at end of file +}; diff --git a/packages/studio/src/app/components/api/apiCard/index.ts b/packages/studio/src/app/components/api/apiCard/index.ts index d426bd3..805ad76 100644 --- a/packages/studio/src/app/components/api/apiCard/index.ts +++ b/packages/studio/src/app/components/api/apiCard/index.ts @@ -1,2 +1 @@ -export * from './apiCard'; export * from './apiCardView'; \ No newline at end of file diff --git a/packages/studio/src/app/components/api/apiDrawer/apiDrawer.tsx b/packages/studio/src/app/components/api/apiDrawer/apiDrawer.tsx index cf966fe..00e3935 100644 --- a/packages/studio/src/app/components/api/apiDrawer/apiDrawer.tsx +++ b/packages/studio/src/app/components/api/apiDrawer/apiDrawer.tsx @@ -1,7 +1,7 @@ import React, { useContext } from 'react'; import { Drawer, DrawerPanelContent, DrawerContent } from '@patternfly/react-core/dist/esm/experimental'; import ApiDataList from '../apiDataList/apiDataList'; -import {ApiCardView} from '../..'; +import {ApiCardView} from '../apiCard/apiCardView'; import ApiDrawerPanelContent from './apiDrawerPanelContent'; import { GlobalContext, GlobalContextObj } from '../../../../context'; import './apiDrawer.css'; diff --git a/packages/studio/src/app/components/api/apiToolbar/apiToolbar.tsx b/packages/studio/src/app/components/api/apiToolbar/apiToolbar.tsx index b06c87e..d36b34b 100644 --- a/packages/studio/src/app/components/api/apiToolbar/apiToolbar.tsx +++ b/packages/studio/src/app/components/api/apiToolbar/apiToolbar.tsx @@ -1,27 +1,247 @@ -import React, { useContext } from 'react'; -import { DataToolbar , DataToolbarItem, DataToolbarContent } from '@patternfly/react-core/dist/esm/experimental'; -import { Button } from '@patternfly/react-core'; -import {ThIcon, ListIcon} from '@patternfly/react-icons'; -import {GlobalContext, GlobalContextObj, DashboardViews} from '../../../../context'; - -export const ApiToolbar: React.FunctionComponent = () => { +import React, { useContext, useState } from "react"; +import { + DataToolbar, + DataToolbarItem, + DataToolbarContent +} from "@patternfly/react-core/dist/esm/experimental"; +import { + Button, + Dropdown, + DropdownPosition, + DropdownToggle, + DropdownItem, + DropdownToggleCheckbox, + InputGroup, + TextInput, + ButtonVariant, + DataToolbarGroup +} from "@patternfly/react-core"; +import { + ThIcon, + ListIcon, + FilterIcon, + SearchIcon, + SortAlphaDownIcon, + SortAlphaUpIcon +} from "@patternfly/react-icons"; +import { Api } from "@apicurio/models"; +import { + GlobalContext, + GlobalContextObj, + DashboardViews, + ToolbarStatus +} from "../../../../context"; +export const ApiToolbar = () => { const globalContext: GlobalContextObj = useContext(GlobalContext); const apiCount = globalContext.store.apis.length; + const [splitButtonDropdownIsOpen, setSplitButtonDropdownIsOpen] = useState( + false + ); + const [isLowerToolbarDropdownOpen, setIsLowerToolbarDropdownOpen] = useState( + false + ); + const [sortIconChanged, setSortIconChanged] = useState(false); + const [currentCategory, setCurrentCategory] = useState("Name"); + + const compare = (direction: string) => { + if (direction === "asc") { + return (a: Api, b: Api) => (a.name > b.name ? 1 : -1); + } else if (direction === "desc") { + return (a: Api, b: Api) => (b.name > a.name ? 1 : -1); + } + return (a: Api, b: Api) => 0; + }; + + const sortAlphaDown = () => { + globalContext.updateApis(globalContext.store.apis.sort(compare("desc"))); + setSortIconChanged(true); + }; + + const sortAlphaUp = () => { + globalContext.updateApis(globalContext.store.apis.sort(compare("asc"))); + setSortIconChanged(false); + }; + + const onNameSelect = (event: any) => { + setCurrentCategory(event.target.innerText); + setIsLowerToolbarDropdownOpen(!isLowerToolbarDropdownOpen); + }; + + const onToolbarDropdownToggle = (event: any) => { + setIsLowerToolbarDropdownOpen(!isLowerToolbarDropdownOpen); + }; + + const selectAll = () => { + let collection: number[] = []; + + // tslint:disable-next-line: prefer-for-of + for (let i = 0; i < globalContext.store.apis.length; i++) { + // tslint:disable-next-line: radix + collection = [...collection, parseInt(globalContext.store.apis[i].id)]; + } + + globalContext.updateToolbarStatus({ + areAllSelected: true, + isChecked: true, + selectedItems: collection + }); + }; + + const splitCheckboxSelectAll = (e: any) => { + const { checked } = e.target; + let collection: number[] = []; + + if (checked) { + // tslint:disable-next-line: prefer-for-of + for (let i = 0; i < globalContext.store.apis.length; i++) { + // tslint:disable-next-line: radix + collection = [...collection, parseInt(globalContext.store.apis[i].id)]; + } + } + + globalContext.updateToolbarStatus({ + areAllSelected: checked, + isChecked: checked, + selectedItems: collection + }); + }; + + const selectNone = (e: any) => { + globalContext.updateToolbarStatus({ + areAllSelected: false, + isChecked: false, + selectedItems: [] + }); + }; + + const onSplitButtonToggle = (isOpen: boolean) => { + setSplitButtonDropdownIsOpen(isOpen); + }; + + const onSplitButtonSelect = (event: any) => { + setSplitButtonDropdownIsOpen(!splitButtonDropdownIsOpen); + }; + + const buildFilterDropdown = () => ( + + + {currentCategory} + + } + isOpen={isLowerToolbarDropdownOpen} + dropdownItems={[ + Name, + Tag + ]} + style={{ width: "100%" }} + /> + + ); + + const buildSelectDropdown = () => { + const numSelected = globalContext.store.toolbarStatus.selectedItems.length; + const allSelected = globalContext.store.toolbarStatus.areAllSelected; + const anySelected = numSelected > 0; + + return ( + + ]} + onToggle={onSplitButtonToggle} + > + {numSelected !== 0 && ( + {numSelected} selected + )} + + } + isOpen={splitButtonDropdownIsOpen} + dropdownItems={[ + + Select none (0 items) + , + + Select all ({globalContext.store.apis.length} items) + + ]} + /> + ); + }; return ( + {buildSelectDropdown()} + + {buildFilterDropdown()} + + + + + - This is where the Data Toolbar should go + - - {apiCount} APIs found @@ -29,7 +249,7 @@ export const ApiToolbar: React.FunctionComponent = () => { - ) -} + ); +}; export default ApiToolbar; diff --git a/packages/studio/src/app/pages/dashboard/dashboard.tsx b/packages/studio/src/app/pages/dashboard/dashboard.tsx index 2c15358..407bc5b 100644 --- a/packages/studio/src/app/pages/dashboard/dashboard.tsx +++ b/packages/studio/src/app/pages/dashboard/dashboard.tsx @@ -1,12 +1,13 @@ import React, {useEffect, useContext, useState} from "react"; import { Button, Drawer, DrawerContent, DrawerContentBody, DrawerPanelContent, Level, LevelItem, Title, PageSection, PageSectionVariants } from '@patternfly/react-core'; import '../../app.css'; -import { ApiDrawer, ApiEmptyState, ApiToolbar } from '../../components'; +import { ApiDrawer, ApiEmptyState, AppToolbar } from '../../components'; import {Link} from 'react-router-dom'; import { GlobalContext, GlobalContextObj } from '../../../context'; import { Services } from './../../common'; import { ApiNotificationDrawer } from './../../components/api/apiNotificationDrawer/apiNotificationDrawer'; import { ApiDesignChange } from "@apicurio/models"; +import ApiToolbar from "src/app/components/api/apiToolbar/apiToolbar"; export const Dashboard = () => { diff --git a/packages/studio/src/context/GlobalContext.tsx b/packages/studio/src/context/GlobalContext.tsx index 7591e73..ddd073a 100644 --- a/packages/studio/src/context/GlobalContext.tsx +++ b/packages/studio/src/context/GlobalContext.tsx @@ -1,92 +1,114 @@ -import React, { createContext } from 'react'; -import {Api, ApiDesignChange, ApiCollaborator} from "@apicurio/models"; +import React, { createContext } from "react"; +import { Api, ApiDesignChange, ApiCollaborator } from "@apicurio/models"; + +export interface ToolbarStatus { + areAllSelected: boolean; + selectedItems: any[]; + isChecked?: boolean; +} export interface GlobalState { - apis: Api[], - collaborators: ApiCollaborator[], - recentActivity: ApiDesignChange[], - dashboardView: DashboardViews, - notificationDrawerExpanded: boolean, - apiDrawerExpanded: boolean, - selectedApiId: string + apis: Api[]; + collaborators: ApiCollaborator[]; + recentActivity: ApiDesignChange[]; + notificationDrawerExpanded: boolean; + dashboardView: DashboardViews; + toolbarStatus: ToolbarStatus; + apiDrawerExpanded: boolean; + selectedApiId: string; } /** * List of views for the dashboard. */ export enum DashboardViews { - list='list', - card='card' + list = "list", + card = "card" } const initialState: GlobalState = { - apiDrawerExpanded: false, - apis: [], - collaborators: [], - dashboardView: DashboardViews.list, - notificationDrawerExpanded: false, - recentActivity: [], - selectedApiId: '' + apiDrawerExpanded: false, + apis: [], + collaborators: [], + dashboardView: DashboardViews.list, + notificationDrawerExpanded: false, + recentActivity: [], + selectedApiId: "", + toolbarStatus: { + areAllSelected: false, + isChecked: false, + selectedItems: [] + } }; export interface GlobalContextObj { - setApiDrawerExpanded: (isExpanded: boolean) => void, - setSelectedApiId: (selectedApiId: string) => void, - store: GlobalState, - setDashboardView: (view: DashboardViews) => void, - setNotificationDrawerExpanded: (isExpanded: boolean) => void, - updateApis: (apis: Api[]) => void, - updateCollaborators: (collaborators: ApiCollaborator[]) => void, - updateRecentActivity: (recentActivity: ApiDesignChange[]) => void, -}; + setApiDrawerExpanded: (isExpanded: boolean) => void; + setSelectedApiId: (selectedApiId: string) => void; + store: GlobalState; + setDashboardView: (view: DashboardViews) => void; + setNotificationDrawerExpanded: (isExpanded: boolean) => void; + updateApis: (apis: Api[]) => void; + updateCollaborators: (collaborators: ApiCollaborator[]) => void; + updateRecentActivity: (recentActivity: ApiDesignChange[]) => void; + updateToolbarStatus: (toolbarStatus: ToolbarStatus) => void; +} export const GlobalContext = createContext({} as GlobalContextObj); export class GlobalContextProvider extends React.Component<{}, GlobalState> { - state: GlobalState = initialState; + state: GlobalState = initialState; - render() { - return ( - - {this.props.children} - - ); - } + render() { + return ( + + {this.props.children} + + ); + } - private setApiDrawerExpanded = (apiDrawerExpanded: boolean) => { - this.setState({apiDrawerExpanded}) - } + private setApiDrawerExpanded = (apiDrawerExpanded: boolean) => { + this.setState({ apiDrawerExpanded }); + }; - private setSelectedApiId = (selectedApiId: string) => { - this.setState({selectedApiId}) - } + private setSelectedApiId = (selectedApiId: string) => { + this.setState({ selectedApiId }); + }; - private setDashboardView = (dashboardView: DashboardViews) => { - this.setState({dashboardView}); - } + private setDashboardView = (dashboardView: DashboardViews) => { + this.setState({ dashboardView }); + }; - private setNotificationDrawerExpanded = (notificationDrawerExpanded: boolean) => { - this.setState({notificationDrawerExpanded}) - } + private setNotificationDrawerExpanded = ( + notificationDrawerExpanded: boolean + ) => { + this.setState({ notificationDrawerExpanded }); + }; - private updateApis = (apis: Api[]) => { - this.setState({apis}) - } + private updateApis = (apis: Api[]) => { + this.setState({ apis }); + }; + + private updateRecentActivity = (recentActivity: ApiDesignChange[]) => { + this.setState({ recentActivity }); + }; - private updateCollaborators = (collaborators: ApiCollaborator[]) => { - this.setState({collaborators}) - } + private updateCollaborators = (collaborators: ApiCollaborator[]) => { + this.setState({ collaborators }); + }; - private updateRecentActivity = (recentActivity: ApiDesignChange[]) => { - this.setState({recentActivity}) - } -}; + private updateToolbarStatus = (toolbarStatus: ToolbarStatus) => { + this.setState({ toolbarStatus }); + }; +}