diff --git a/ui-common/src/components/OasModal/OasModal.tsx b/ui-common/src/components/OasModal/OasModal.tsx index 5c5e14496..00bbac1e5 100644 --- a/ui-common/src/components/OasModal/OasModal.tsx +++ b/ui-common/src/components/OasModal/OasModal.tsx @@ -8,6 +8,7 @@ import openApiLogo from 'assets/openApiLogo.png' import { redocThemeOptions } from "./redocThemeOptions"; import React from "react"; import { Select } from "../UI/Select"; +import { TOAST_CONTAINER_ID } from "../../configs/Consts"; const modalStyle = { @@ -43,7 +44,7 @@ const OasModal = ({ openModal, handleCloseModal, getOasServices, getOasByService const data = await getOasByService(selectedService ? selectedService : oasServices[0]); setSelectedServiceSpec(data); } catch (e) { - toast.error("Error occurred while fetching service OAS spec"); + toast.error("Error occurred while fetching service OAS spec", { containerId: TOAST_CONTAINER_ID }); console.error(e); } }; diff --git a/ui-common/src/components/ServiceMapModal/ServiceMapModal.module.sass b/ui-common/src/components/ServiceMapModal/ServiceMapModal.module.sass index 9c74e4b35..e481416a7 100644 --- a/ui-common/src/components/ServiceMapModal/ServiceMapModal.module.sass +++ b/ui-common/src/components/ServiceMapModal/ServiceMapModal.module.sass @@ -53,7 +53,7 @@ & .servicesFilterList overflow-y: auto - height: 92% + height: calc(100% - 30px - 5px) .separtorLine margin-top: 10px diff --git a/ui-common/src/components/ServiceMapModal/ServiceMapModal.tsx b/ui-common/src/components/ServiceMapModal/ServiceMapModal.tsx index a81ea6a0f..5f6024541 100644 --- a/ui-common/src/components/ServiceMapModal/ServiceMapModal.tsx +++ b/ui-common/src/components/ServiceMapModal/ServiceMapModal.tsx @@ -15,6 +15,7 @@ import { GraphData, ServiceMapGraph } from "./ServiceMapModalTypes" import { ResizableBox } from "react-resizable" import "react-resizable/css/styles.css" import { Utils } from "../../helpers/Utils"; +import { TOAST_CONTAINER_ID } from "../../configs/Consts"; const modalStyle = { position: 'absolute', @@ -46,12 +47,12 @@ const LegentLabel: React.FC = ({ color, name }) => { } const protocols = [ - { key: "http", value: "HTTP", component: }, - { key: "http/2", value: "HTTP/2", component: }, - { key: "grpc", value: "gRPC", component: }, - { key: "amqp", value: "AMQP", component: }, - { key: "kafka", value: "KAFKA", component: }, - { key: "redis", value: "REDIS", component: },] + { key: "HTTP", value: "HTTP", component: }, + { key: "HTTP/2", value: "HTTP/2", component: }, + { key: "gRPC", value: "gRPC", component: }, + { key: "AMQP", value: "AMQP", component: }, + { key: "KAFKA", value: "KAFKA", component: }, + { key: "REDIS", value: "REDIS", component: },] interface ServiceMapModalProps { @@ -65,8 +66,8 @@ export const ServiceMapModal: React.FC = ({ isOpen, onClos const commonClasses = useCommonStyles(); const [isLoading, setIsLoading] = useState(true); const [graphData, setGraphData] = useState({ nodes: [], edges: [] }); - const [filteredProtocols, setFilteredProtocols] = useState(protocols.map(x => x.key)) - const [filteredServices, setFilteredServices] = useState([]) + const [checkedProtocols, setCheckedProtocols] = useState(protocols.map(x => x.key)) + const [checkedServices, setCheckedServices] = useState([]) const [serviceMapApiData, setServiceMapApiData] = useState({ edges: [], nodes: [] }) const [servicesSearchVal, setServicesSearchVal] = useState("") const [graphOptions, setGraphOptions] = useState(ServiceMapOptions); @@ -89,7 +90,7 @@ export const ServiceMapModal: React.FC = ({ isOpen, onClos setGraphData(newGraphData) } catch (ex) { - toast.error("An error occurred while loading Mizu Service Map, see console for mode details"); + toast.error("An error occurred while loading Mizu Service Map, see console for mode details", { containerId: TOAST_CONTAINER_ID }); console.error(ex); } finally { setIsLoading(false) @@ -131,21 +132,20 @@ export const ServiceMapModal: React.FC = ({ isOpen, onClos }, [serviceMapApiData]) const filterServiceMap = (newProtocolsFilters?: any[], newServiceFilters?: string[]) => { - const filterProt = newProtocolsFilters || filteredProtocols - const filterService = newServiceFilters || filteredServices || getServicesForFilter.map(x => x.key) - setFilteredProtocols(filterProt) - setFilteredServices(filterService) + const filterProt = newProtocolsFilters || checkedProtocols + const filterService = newServiceFilters || checkedServices + setCheckedProtocols(filterProt) + setCheckedServices(filterService) const newGraphData: GraphData = { nodes: serviceMapApiData.nodes?.map(mapNodesDatatoGraph).filter(node => filterService.includes(node.label)), - edges: serviceMapApiData.edges?.filter(edge => filterProt.includes(edge.protocol.name)).map(mapEdgesDatatoGraph) + edges: serviceMapApiData.edges?.filter(edge => filterProt.includes(edge.protocol.abbr)).map(mapEdgesDatatoGraph) } setGraphData(newGraphData); } useEffect(() => { - const resolvedServices = getServicesForFilter.map(x => x.key).filter(serviceName => !Utils.isIpAddress(serviceName)) - setFilteredServices(resolvedServices) - filterServiceMap(filteredProtocols, resolvedServices) + if (checkedServices.length > 0) return // only after refresh + filterServiceMap(checkedProtocols, getServicesForFilter.map(x => x.key).filter(serviceName => !Utils.isIpAddress(serviceName))) }, [getServicesForFilter]) useEffect(() => { @@ -182,14 +182,14 @@ export const ServiceMapModal: React.FC = ({ isOpen, onClos
+ checkedValues={checkedProtocols} setCheckedValues={filterServiceMap} tableClassName={styles.filters} />
setServicesSearchVal(event.target.value)} />
filterServiceMap(null, newServicesForFilter)} /> + checkBoxWidth="5%" checkedValues={checkedServices} setCheckedValues={(newServicesForFilter) => filterServiceMap(null, newServicesForFilter)} />
diff --git a/ui-common/src/components/UI/SelectList.tsx b/ui-common/src/components/UI/SelectList.tsx index e9bb02a07..4687ba816 100644 --- a/ui-common/src/components/UI/SelectList.tsx +++ b/ui-common/src/components/UI/SelectList.tsx @@ -1,4 +1,4 @@ -import React, { useMemo } from "react"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; import Radio from "./Radio"; import styles from './style/SelectList.module.sass' import NoDataMessage from "./NoDataMessage"; @@ -19,12 +19,16 @@ export interface Props { const SelectList: React.FC = ({ items, tableName, checkedValues = [], multiSelect = true, searchValue = "", setCheckedValues, tableClassName, checkBoxWidth = 50 }) => { const noItemsMessage = "No items to show"; - const enabledItemsLength = useMemo(() => items.filter(item => !item.disabled).length, [items]); + const [headerChecked, setHeaderChecked] = useState(false) const filteredValues = useMemo(() => { return items.filter((listValue) => listValue?.value?.includes(searchValue)); }, [items, searchValue]) + const filteredValuesKeys = useMemo(() => { + return filteredValues.map(x => x.key) + }, [filteredValues]) + const toggleValue = (checkedKey) => { if (!multiSelect) { const newCheckedValues = []; @@ -34,25 +38,31 @@ const SelectList: React.FC = ({ items, tableName, checkedValues = [], mul else { const newCheckedValues = [...checkedValues]; let index = newCheckedValues.indexOf(checkedKey); + if (index > -1) newCheckedValues.splice(index, 1); else newCheckedValues.push(checkedKey); + setCheckedValues(newCheckedValues); } } - const toggleAll = () => { - const newCheckedValues = [...checkedValues]; - if (newCheckedValues.length === enabledItemsLength) setCheckedValues([]); - else { - items.forEach((obj) => { - if (!obj.disabled && !newCheckedValues.includes(obj.key)) - newCheckedValues.push(obj.key); - }) - setCheckedValues(newCheckedValues); + useEffect(() => { + const setAllChecked = filteredValuesKeys.every(val => checkedValues.includes(val)) + setHeaderChecked(setAllChecked) + }, [filteredValuesKeys, checkedValues]) + + const toggleAll = useCallback((shouldCheckAll) => { + let newChecked = checkedValues.filter(x => !filteredValuesKeys.includes(x)) + + if (shouldCheckAll) { + const disabledItems = items.filter(i => i.disabled).map(x => x.key) + newChecked = [...filteredValuesKeys, ...newChecked].filter(x => !disabledItems.includes(x)) } - } + + setCheckedValues(newChecked) + }, [searchValue, checkedValues, filteredValuesKeys]) const dataFieldFunc = (listValue) => listValue.component ? listValue.component : @@ -60,8 +70,8 @@ const SelectList: React.FC = ({ items, tableName, checkedValues = [], mul const tableHead = multiSelect ? - + toggleAll(isChecked)} /> {tableName} : @@ -70,7 +80,7 @@ const SelectList: React.FC = ({ items, tableName, checkedValues = [], mul const tableBody = filteredValues.length === 0 ? - + diff --git a/ui-common/src/components/UI/style/SelectList.module.sass b/ui-common/src/components/UI/style/SelectList.module.sass index c822d5dd0..618ce93f6 100644 --- a/ui-common/src/components/UI/style/SelectList.module.sass +++ b/ui-common/src/components/UI/style/SelectList.module.sass @@ -1,25 +1,26 @@ @import '../../../variables.module' .selectListTable + overflow: auto + height: 100% + table width: 100% margin-top: 20px - height: 100% - - tbody - display: block + border-collapse: collapse th color: $blue-gray text-align: left padding: 10px + position: sticky + top: 0 + background: $main-background-color tr border-bottom-width: 1px border-bottom-color: $data-background-color border-bottom-style: solid - display: table - table-layout: fixed width: 100% td