diff --git a/web/packages/shared/components/ClusterDropdown/ClusterDropdown.tsx b/web/packages/shared/components/ClusterDropdown/ClusterDropdown.tsx new file mode 100644 index 0000000000000..7e73a61ee1f21 --- /dev/null +++ b/web/packages/shared/components/ClusterDropdown/ClusterDropdown.tsx @@ -0,0 +1,179 @@ +/** + * Teleport + * Copyright (C) 2023 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import React, { useState, useEffect } from 'react'; +import { useHistory } from 'react-router'; +import { ButtonSecondary, Flex, Menu, MenuItem, Text } from 'design'; +import { ChevronDown } from 'design/Icon'; +import cfg from 'teleport/config'; +import { Cluster } from 'teleport/services/clusters'; + +import { HoverTooltip } from 'shared/components/ToolTip'; + +export interface ClusterDropdownProps { + clusterLoader: ClusterLoader; + clusterId: string; + /* + * onChange is an optional prop. If onChange is not passed, it will use the built in "changeCluster" function + */ + onChange?: (newValue: string) => void; + /* + * onError is required because this dropdown can be placed on any page, it does not display its own error + * messages. Even if using the internal "loadClusters", we will pass the error back to be consumed by the parent. + */ + onError: (errorMessage: string) => void; +} + +interface ClusterLoader { + fetchClusters: ( + signal?: AbortSignal, + fromCache?: boolean + ) => Promise; + clusters: Cluster[]; +} + +function createOptions(clusters: Cluster[]) { + return clusters.map(cluster => ({ + value: cluster.clusterId, + label: cluster.clusterId, + })); +} + +export function ClusterDropdown({ + clusterLoader, + clusterId, + onChange, + onError, +}: ClusterDropdownProps) { + const initialClusters = clusterLoader.clusters; + const [options, setOptions] = React.useState( + createOptions(initialClusters) + ); + const history = useHistory(); + const [anchorEl, setAnchorEl] = useState(null); + + const selectedOption = { + value: clusterId, + label: clusterId, + }; + + function loadClusters(signal: AbortSignal) { + onError(''); + try { + return clusterLoader.fetchClusters(signal); + } catch (err) { + onError(err.message); + } + } + + function changeCluster(clusterId: string) { + const newPathName = cfg.getClusterRoute(clusterId); + + const oldPathName = cfg.getClusterRoute(selectedOption.value); + + const newPath = history.location.pathname.replace(oldPathName, newPathName); + + // keep current view just change the clusterId + history.push(newPath); + } + + function onChangeOption(clusterId: string) { + if (onChange) { + onChange(clusterId); + } else { + changeCluster(clusterId); + } + handleClose(); + } + + useEffect(() => { + const signal = new AbortController(); + async function getOptions() { + try { + const res = await loadClusters(signal.signal); + setOptions(createOptions(res)); + } catch (err) { + onError(err.message); + } + } + + getOptions(); + return () => { + signal.abort(); + }; + }, []); + + const handleOpen = event => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + if (options.length < 1) { + return null; + } + + return ( + + + props.theme.colors.spotBackground[0]}; + `} + textTransform="none" + size="small" + onClick={handleOpen} + > + {selectedOption.label} + + + + `margin-top: 36px;`} + transformOrigin={{ + vertical: 'top', + horizontal: 'left', + }} + anchorOrigin={{ + vertical: 'bottom', + horizontal: 'left', + }} + anchorEl={anchorEl} + open={Boolean(anchorEl)} + onClose={handleClose} + > + {options.map(cluster => ( + onChangeOption(cluster.value)} + > + + {cluster.label} + + + ))} + + + ); +} + +type Option = { value: string; label: string }; diff --git a/web/packages/shared/components/UnifiedResources/FilterPanel.tsx b/web/packages/shared/components/UnifiedResources/FilterPanel.tsx index afba5cbba8290..3733dd90441ff 100644 --- a/web/packages/shared/components/UnifiedResources/FilterPanel.tsx +++ b/web/packages/shared/components/UnifiedResources/FilterPanel.tsx @@ -65,6 +65,11 @@ interface FilterPanelProps { expandAllLabels: boolean; setExpandAllLabels: (expandAllLabels: boolean) => void; hideViewModeOptions: boolean; + /* + * ClusterDropdown is an optional prop to add a ClusterDropdown to the + * FilterPanel component. This is useful to turn off in Connect and use on web only + */ + ClusterDropdown?: JSX.Element; } export function FilterPanel({ @@ -79,6 +84,7 @@ export function FilterPanel({ expandAllLabels, setExpandAllLabels, hideViewModeOptions, + ClusterDropdown = null, }: FilterPanelProps) { const { sort, kinds } = params; @@ -120,6 +126,7 @@ export function FilterPanel({ availableKinds={availableKinds} kindsFromParams={kinds || []} /> + {ClusterDropdown} {BulkActions} diff --git a/web/packages/shared/components/UnifiedResources/UnifiedResources.tsx b/web/packages/shared/components/UnifiedResources/UnifiedResources.tsx index 60ae77a2c12d8..c361fdcc637bd 100644 --- a/web/packages/shared/components/UnifiedResources/UnifiedResources.tsx +++ b/web/packages/shared/components/UnifiedResources/UnifiedResources.tsx @@ -147,6 +147,11 @@ export interface UnifiedResourcesProps { */ pinning: UnifiedResourcesPinning; availableKinds: FilterKind[]; + /* + * ClusterDropdown is an optional prop to add a ClusterDropdown to the + * FilterPanel component. This is useful to turn off in Connect and use on web only + */ + ClusterDropdown?: JSX.Element; setParams(params: UnifiedResourcesQueryParams): void; /** A list of actions that can be performed on the selected items. */ bulkActions?: BulkAction[]; @@ -175,6 +180,7 @@ export function UnifiedResources(props: UnifiedResourcesProps) { unifiedResourcePreferencesAttempt, updateUnifiedResourcesPreferences, unifiedResourcePreferences, + ClusterDropdown, bulkActions = [], } = props; @@ -439,6 +445,7 @@ export function UnifiedResources(props: UnifiedResourcesProps) { currentViewMode={unifiedResourcePreferences.viewMode} setCurrentViewMode={selectViewMode} expandAllLabels={expandAllLabels} + ClusterDropdown={ClusterDropdown} setExpandAllLabels={expandAllLabels => { setLabelsViewMode( expandAllLabels diff --git a/web/packages/teleport/src/Audit/Audit.tsx b/web/packages/teleport/src/Audit/Audit.tsx index e2818de2732b8..85e86900701ab 100644 --- a/web/packages/teleport/src/Audit/Audit.tsx +++ b/web/packages/teleport/src/Audit/Audit.tsx @@ -16,10 +16,11 @@ * along with this program. If not, see . */ -import React from 'react'; +import React, { useState } from 'react'; import { Danger } from 'design/Alert'; import { Indicator, Box } from 'design'; +import { ClusterDropdown } from 'shared/components/ClusterDropdown/ClusterDropdown'; import RangePicker from 'teleport/components/EventRangePicker'; import { @@ -53,7 +54,9 @@ export function Audit(props: State) { clusterId, fetchMore, fetchStatus, + ctx, } = props; + const [errorMessage, setErrorMessage] = useState(''); return ( @@ -68,6 +71,16 @@ export function Audit(props: State) { {attempt.status === 'failed' && {attempt.statusText} } + {!errorMessage && ( + + + + )} + {errorMessage && {errorMessage}} {attempt.status === 'processing' && ( diff --git a/web/packages/teleport/src/Audit/__snapshots__/Audit.story.test.tsx.snap b/web/packages/teleport/src/Audit/__snapshots__/Audit.story.test.tsx.snap index 65ba878cf6eb6..c7a60a4a80f04 100644 --- a/web/packages/teleport/src/Audit/__snapshots__/Audit.story.test.tsx.snap +++ b/web/packages/teleport/src/Audit/__snapshots__/Audit.story.test.tsx.snap @@ -12852,6 +12852,9 @@ exports[`loaded audit log screen 1`] = ` +
diff --git a/web/packages/teleport/src/Audit/useAuditEvents.ts b/web/packages/teleport/src/Audit/useAuditEvents.ts index cccf1a4f6150a..0dba36b065ef1 100644 --- a/web/packages/teleport/src/Audit/useAuditEvents.ts +++ b/web/packages/teleport/src/Audit/useAuditEvents.ts @@ -98,6 +98,7 @@ export default function useAuditEvents( range, setRange, rangeOptions, + ctx, }; } diff --git a/web/packages/teleport/src/Console/DocumentNodes/DocumentNodes.story.test.tsx b/web/packages/teleport/src/Console/DocumentNodes/DocumentNodes.story.test.tsx index 89e8bf03b2b5d..b07b045eca09d 100644 --- a/web/packages/teleport/src/Console/DocumentNodes/DocumentNodes.story.test.tsx +++ b/web/packages/teleport/src/Console/DocumentNodes/DocumentNodes.story.test.tsx @@ -24,11 +24,13 @@ import { Document, createContext } from './DocumentNodes.story'; test('render DocumentNodes', async () => { const ctx = createContext(); - jest.spyOn(ctx, 'fetchClusters'); + jest.spyOn(ctx.clustersService, 'fetchClusters'); jest.spyOn(ctx.nodesService, 'fetchNodes'); const { container } = render(); - await waitFor(() => expect(ctx.fetchClusters).toHaveBeenCalledTimes(1)); + await waitFor(() => + expect(ctx.clustersService.fetchClusters).toHaveBeenCalledTimes(1) + ); await waitFor(() => expect(ctx.nodesService.fetchNodes).toHaveBeenCalledTimes(1) ); diff --git a/web/packages/teleport/src/Console/DocumentNodes/DocumentNodes.tsx b/web/packages/teleport/src/Console/DocumentNodes/DocumentNodes.tsx index 3eec3d5e9a3aa..624553bde7a74 100644 --- a/web/packages/teleport/src/Console/DocumentNodes/DocumentNodes.tsx +++ b/web/packages/teleport/src/Console/DocumentNodes/DocumentNodes.tsx @@ -16,9 +16,12 @@ * along with this program. If not, see . */ -import React from 'react'; +import React, { useState } from 'react'; import styled from 'styled-components'; -import { Indicator, Flex, Box } from 'design'; +import { Indicator, Box, Text } from 'design'; +import { Danger } from 'design/Alert'; + +import { ClusterDropdown } from 'shared/components/ClusterDropdown/ClusterDropdown'; import NodeList from 'teleport/components/NodeList'; import ErrorMessage from 'teleport/components/AgentErrorMessage'; @@ -26,7 +29,6 @@ import Document from 'teleport/Console/Document'; import * as stores from 'teleport/Console/stores/types'; -import ClusterSelector from './ClusterSelector'; import useNodes from './useNodes'; type Props = { @@ -36,6 +38,7 @@ type Props = { export default function DocumentNodes(props: Props) { const { doc, visible } = props; + const [clusterDropdownError, setClusterDropdownError] = useState(''); const { fetchedData, fetchNext, @@ -53,6 +56,7 @@ export default function DocumentNodes(props: Props) { getNodeSshLogins, onLabelClick, pageIndicators, + consoleCtx, } = useNodes(doc); function onLoginMenuSelect( @@ -79,15 +83,16 @@ export default function DocumentNodes(props: Props) { return ( - - + Clusters: + - + + {clusterDropdownError && {clusterDropdownError}} {attempt.status === 'processing' && ( diff --git a/web/packages/teleport/src/Console/DocumentNodes/__snapshots__/DocumentNodes.story.test.tsx.snap b/web/packages/teleport/src/Console/DocumentNodes/__snapshots__/DocumentNodes.story.test.tsx.snap index f1c8e3a8b2bfe..313dd19a3d77c 100644 --- a/web/packages/teleport/src/Console/DocumentNodes/__snapshots__/DocumentNodes.story.test.tsx.snap +++ b/web/packages/teleport/src/Console/DocumentNodes/__snapshots__/DocumentNodes.story.test.tsx.snap @@ -24,37 +24,31 @@ exports[`render DocumentNodes 1`] = ` .c5 { box-sizing: border-box; - margin-bottom: 24px; + margin-bottom: 8px; } -.c7 { - box-sizing: border-box; - margin-right: 20px; - width: 336px; -} - -.c12 { +.c9 { box-sizing: border-box; width: 100%; } -.c14 { +.c11 { box-sizing: border-box; } -.c16 { +.c13 { box-sizing: border-box; margin-right: 16px; width: 100%; } -.c22 { +.c19 { box-sizing: border-box; padding-left: 24px; padding-right: 24px; } -.c37 { +.c34 { line-height: 1.5; margin: 0; display: inline-flex; @@ -82,22 +76,22 @@ exports[`render DocumentNodes 1`] = ` height: 24px; } -.c37:hover, -.c37:focus { +.c34:hover, +.c34:focus { background: rgba(255,255,255,0.07); } -.c37:active { +.c34:active { background: rgba(255,255,255,0.13); } -.c37:disabled { +.c34:disabled { background: rgba(255,255,255,0.12); color: rgba(255,255,255,0.3); cursor: auto; } -.c41 { +.c38 { align-items: center; border: none; cursor: pointer; @@ -119,25 +113,25 @@ exports[`render DocumentNodes 1`] = ` margin-right: 0px; } -.c41:disabled { +.c38:disabled { color: rgba(255,255,255,0.36); } -.c41:disabled { +.c38:disabled { color: rgba(255,255,255,0.36); cursor: default; } -.c41:hover:enabled, -.c41:focus:enabled { +.c38:hover:enabled, +.c38:focus:enabled { background: rgba(255,255,255,0.13); } -.c41:active:enabled { +.c38:active:enabled { background: rgba(255,255,255,0.18); } -.c43 { +.c40 { align-items: center; border: none; cursor: pointer; @@ -158,25 +152,32 @@ exports[`render DocumentNodes 1`] = ` margin-left: 0px; } -.c43:disabled { +.c40:disabled { color: rgba(255,255,255,0.36); } -.c43:disabled { +.c40:disabled { color: rgba(255,255,255,0.36); cursor: default; } -.c43:hover:enabled, -.c43:focus:enabled { +.c40:hover:enabled, +.c40:focus:enabled { background: rgba(255,255,255,0.13); } -.c43:active:enabled { +.c40:active:enabled { background: rgba(255,255,255,0.18); } -.c28 { +.c6 { + overflow: hidden; + text-overflow: ellipsis; + font-size: 12px; + margin: 0px; +} + +.c25 { overflow: hidden; text-overflow: ellipsis; font-weight: 400; @@ -185,7 +186,7 @@ exports[`render DocumentNodes 1`] = ` margin: 0px; } -.c32 { +.c29 { overflow: hidden; text-overflow: ellipsis; font-weight: 400; @@ -196,13 +197,13 @@ exports[`render DocumentNodes 1`] = ` color: #FFFFFF; } -.c30 { +.c27 { display: inline-flex; align-items: center; justify-content: center; } -.c38 { +.c35 { display: inline-flex; align-items: center; justify-content: center; @@ -211,7 +212,7 @@ exports[`render DocumentNodes 1`] = ` margin-right: -8px; } -.c35 { +.c32 { box-sizing: border-box; border-radius: 10px; display: inline-block; @@ -226,63 +227,49 @@ exports[`render DocumentNodes 1`] = ` margin-right: 4px; } -.c8 { - color: #FFFFFF; - display: block; - font-size: 12px; - width: 100%; - margin-bottom: 4px; -} - .c1 { display: flex; } -.c6 { - display: flex; - align-items: end; - justify-content: space-between; -} - -.c13 { +.c10 { display: flex; align-items: center; justify-content: space-between; } -.c15 { +.c12 { display: flex; align-items: center; } -.c23 { +.c20 { display: flex; align-items: center; gap: 8px; } -.c34 { +.c31 { display: flex; flex-wrap: wrap; } -.c40 { +.c37 { display: flex; justify-content: flex-end; } -.c24 { +.c21 { position: relative; display: flex; align-items: center; cursor: pointer; } -.c24[disabled] { +.c21[disabled] { cursor: default; } -.c27 { +.c24 { width: 32px; height: 12px; border-radius: 12px; @@ -291,7 +278,7 @@ exports[`render DocumentNodes 1`] = ` flex-shrink: 0; } -.c27:before { +.c24:before { content: ''; position: absolute; top: 50%; @@ -302,29 +289,45 @@ exports[`render DocumentNodes 1`] = ` background: #9F85FF; } -.c25 { +.c22 { opacity: 0; position: absolute; cursor: inherit; } -.c25:checked + .c26 { +.c22:checked + .c23 { background: rgba(255,255,255,0.13); } -.c25:checked + .c26:before { +.c22:checked + .c23:before { transform: translate(16px,-50%); } -.c25:disabled + .c26 { +.c22:disabled + .c23 { background: #222C59; } -.c25:disabled + .c26:before { +.c22:disabled + .c23:before { background: #455a64; } -.c33 { +.c28 { + height: 18px; + width: 18px; + color: inherit; +} + +.c26 { + vertical-align: middle; + display: inline-block; + height: 18px; +} + +.c26:hover { + cursor: pointer; +} + +.c30 { background: #222C59; border-collapse: collapse; border-spacing: 0; @@ -333,39 +336,39 @@ exports[`render DocumentNodes 1`] = ` width: 100%; } -.c33 > thead > tr > th, -.c33 > tbody > tr > th, -.c33 > tfoot > tr > th, -.c33 > thead > tr > td, -.c33 > tbody > tr > td, -.c33 > tfoot > tr > td { +.c30 > thead > tr > th, +.c30 > tbody > tr > th, +.c30 > tfoot > tr > th, +.c30 > thead > tr > td, +.c30 > tbody > tr > td, +.c30 > tfoot > tr > td { padding: 8px 8px; vertical-align: middle; } -.c33 > thead > tr > th:first-child, -.c33 > tbody > tr > th:first-child, -.c33 > tfoot > tr > th:first-child, -.c33 > thead > tr > td:first-child, -.c33 > tbody > tr > td:first-child, -.c33 > tfoot > tr > td:first-child { +.c30 > thead > tr > th:first-child, +.c30 > tbody > tr > th:first-child, +.c30 > tfoot > tr > th:first-child, +.c30 > thead > tr > td:first-child, +.c30 > tbody > tr > td:first-child, +.c30 > tfoot > tr > td:first-child { padding-left: 24px; } -.c33 > thead > tr > th:last-child, -.c33 > tbody > tr > th:last-child, -.c33 > tfoot > tr > th:last-child, -.c33 > thead > tr > td:last-child, -.c33 > tbody > tr > td:last-child, -.c33 > tfoot > tr > td:last-child { +.c30 > thead > tr > th:last-child, +.c30 > tbody > tr > th:last-child, +.c30 > tfoot > tr > th:last-child, +.c30 > thead > tr > td:last-child, +.c30 > tbody > tr > td:last-child, +.c30 > tfoot > tr > td:last-child { padding-right: 24px; } -.c33 > tbody > tr > td { +.c30 > tbody > tr > td { vertical-align: middle; } -.c33 > thead > tr > th { +.c30 > thead > tr > th { background: rgba(255,255,255,0.07); color: #FFFFFF; cursor: pointer; @@ -379,24 +382,24 @@ exports[`render DocumentNodes 1`] = ` white-space: nowrap; } -.c33 > thead > tr > th svg { +.c30 > thead > tr > th svg { height: 12px; } -.c33 > tbody > tr > td { +.c30 > tbody > tr > td { color: #FFFFFF; line-height: 16px; } -.c33 tbody tr { +.c30 tbody tr { border-bottom: 1px solid rgb(49,58,100); } -.c33 tbody tr:hover { +.c30 tbody tr:hover { background-color: rgba(255,255,255,0.07); } -.c11 { +.c8 { padding: 16px 24px; display: flex; height: 24px; @@ -408,7 +411,7 @@ exports[`render DocumentNodes 1`] = ` border-top-right-radius: 8px; } -.c39 { +.c36 { padding: 16px 24px; display: flex; height: 24px; @@ -419,29 +422,29 @@ exports[`render DocumentNodes 1`] = ` border-top: 1px solid rgba(255,255,255,0.07); } -.c10 { +.c7 { box-shadow: 0px 2px 1px -1px rgba(0,0,0,0.2),0px 1px 1px rgba(0,0,0,0.14),0px 1px 3px rgba(0,0,0,0.12); overflow: hidden; border-radius: 8px; } -.c36 { +.c33 { cursor: pointer; } -.c36:hover { +.c33:hover { background-color: rgba(255,255,255,0.13); } -.c42 svg { +.c39 svg { font-size: 20px; } -.c42 svg:before { +.c39 svg:before { padding-left: 1px; } -.c21 { +.c18 { position: relative; height: 100%; right: 0; @@ -452,7 +455,7 @@ exports[`render DocumentNodes 1`] = ` border-radius: 200px; } -.c20 { +.c17 { position: absolute; height: 100%; right: 0; @@ -463,7 +466,7 @@ exports[`render DocumentNodes 1`] = ` border-radius: 200px; } -.c18 { +.c15 { position: relative; display: flex; overflow: hidden; @@ -473,14 +476,14 @@ exports[`render DocumentNodes 1`] = ` background: transparent; } -.c17 { +.c14 { background: #0C143D; border-radius: 200px; width: 100%; height: 32px; } -.c19 { +.c16 { border: none; outline: none; box-sizing: border-box; @@ -495,198 +498,18 @@ exports[`render DocumentNodes 1`] = ` padding-right: 184px; } -.c19:hover, -.c19:focus, -.c19:active { +.c16:hover, +.c16:focus, +.c16:active { color: #FFFFFF; background: rgba(255,255,255,0.07); } -.c19::placeholder { +.c16::placeholder { color: rgba(255,255,255,0.54); font-size: 12px; } -.c31 { - height: 18px; - width: 18px; - color: inherit; -} - -.c29 { - vertical-align: middle; - display: inline-block; - height: 18px; -} - -.c29:hover { - cursor: pointer; -} - -.c9 .react-select-container { - box-sizing: border-box; - display: block; - font-size: 14px; - outline: none; - width: 100%; - color: #FFFFFF; - background-color: transparent; - margin-bottom: 0px; - border-radius: 4px; -} - -.c9 .react-select__control { - outline: none; - min-height: 40px; - height: fit-content; - border: 1px solid rgba(255,255,255,0.54); - border-radius: 4px; - background-color: transparent; - box-shadow: none; -} - -.c9 .react-select__control .react-select__dropdown-indicator { - color: rgba(255,255,255,0.54); -} - -.c9 .react-select__control:hover, -.c9 .react-select__control:focus, -.c9 .react-select__control:active { - border: 1px solid rgba(255,255,255,0.72); - background-color: rgba(255,255,255,0.07); - cursor: pointer; -} - -.c9 .react-select__control:hover .react-select__dropdown-indicator, -.c9 .react-select__control:focus .react-select__dropdown-indicator, -.c9 .react-select__control:active .react-select__dropdown-indicator { - color: #FFFFFF; -} - -.c9 .react-select__control .react-select__indicator:hover, -.c9 .react-select__control .react-select__dropdown-indicator:hover, -.c9 .react-select__control .react-select__indicator:focus, -.c9 .react-select__control .react-select__dropdown-indicator:focus, -.c9 .react-select__control .react-select__indicator:active, -.c9 .react-select__control .react-select__dropdown-indicator:active { - color: #FFFFFF; -} - -.c9 .react-select__control--is-focused { - border-color: rgba(255,255,255,0.72); - background-color: rgba(255,255,255,0.07); - cursor: pointer; -} - -.c9 .react-select__control--is-focused .react-select__dropdown-indicator { - color: #FFFFFF; -} - -.c9 .react-select__single-value { - color: #FFFFFF; -} - -.c9 .react-select__placeholder { - color: rgba(255,255,255,0.54); -} - -.c9 .react-select__multi-value { - background-color: rgba(255,255,255,0.13); -} - -.c9 .react-select__multi-value .react-select__multi-value__label { - color: #FFFFFF; - padding: 0 6px; -} - -.c9 .react-select__multi-value .react-select__multi-value__remove { - color: #FFFFFF; -} - -.c9 .react-select__multi-value .react-select__multi-value__remove:hover { - background-color: rgba(255,255,255,0.07); - color: #FF6257; -} - -.c9 .react-select__option:hover { - cursor: pointer; - background-color: rgba(255,255,255,0.07); -} - -.c9 .react-select__option--is-focused { - background-color: rgba(255,255,255,0.07); -} - -.c9 .react-select__option--is-focused:hover { - cursor: pointer; - background-color: rgba(255,255,255,0.07); -} - -.c9 .react-select__option--is-selected { - background-color: rgba(255,255,255,0.13); - color: inherit; - font-weight: 500; -} - -.c9 .react-select__option--is-selected:hover { - background-color: rgba(255,255,255,0.13); -} - -.c9 .react-select__clear-indicator { - color: rgba(255,255,255,0.72); -} - -.c9 .react-select__clear-indicator:hover, -.c9 .react-select__clear-indicator:focus { - background-color: rgba(255,255,255,0.07); -} - -.c9 .react-select__clear-indicator:hover svg, -.c9 .react-select__clear-indicator:focus svg { - color: #FF6257; -} - -.c9 .react-select__menu { - margin-top: 0px; - background-color: #344179; - box-shadow: 0px 5px 5px -3px rgba(0,0,0,0.2),0px 8px 10px 1px rgba(0,0,0,0.14),0px 3px 14px 2px rgba(0,0,0,0.12); -} - -.c9 .react-select__menu .react-select__menu-list::-webkit-scrollbar-thumb { - background: rgba(255,255,255,0.13); - border-radius: 4px; -} - -.c9 .react-select__indicator-separator { - display: none; -} - -.c9 .react-select__loading-indicator { - display: none; -} - -.c9 .react-select--is-disabled, -.c9 .react-select__control--is-disabled { - color: rgba(255,255,255,0.36); - border: 1px solid rgba(255,255,255,0.36); -} - -.c9 .react-select--is-disabled .react-select__single-value, -.c9 .react-select__control--is-disabled .react-select__single-value, -.c9 .react-select--is-disabled .react-select__placeholder, -.c9 .react-select__control--is-disabled .react-select__placeholder { - color: rgba(255,255,255,0.36); -} - -.c9 .react-select--is-disabled .react-select__indicator, -.c9 .react-select__control--is-disabled .react-select__indicator { - color: rgba(255,255,255,0.36); -} - -.c9 .react-select__input { - color: #FFFFFF; -} - .c4 { flex-direction: column; display: flex; @@ -714,149 +537,75 @@ exports[`render DocumentNodes 1`] = ` class="c3 c4" >
- -
-
-
-
-
- cluseter-1 -
-
-
- -
-
-
-
-
- - -
-
-
-
+ Clusters: