diff --git a/web/packages/shared/components/UnifiedResources/FilterPanel.tsx b/web/packages/shared/components/UnifiedResources/FilterPanel.tsx index c15ca772582ad..94f0868cad89a 100644 --- a/web/packages/shared/components/UnifiedResources/FilterPanel.tsx +++ b/web/packages/shared/components/UnifiedResources/FilterPanel.tsx @@ -18,7 +18,7 @@ import React, { useState } from 'react'; import styled from 'styled-components'; import { ButtonBorder, ButtonPrimary, ButtonSecondary } from 'design/Button'; import { SortDir } from 'design/DataTable/types'; -import { Text, Flex, Box } from 'design'; +import { Text, Flex } from 'design'; import Menu, { MenuItem } from 'design/Menu'; import { StyledCheckbox } from 'design/Checkbox'; import { @@ -62,6 +62,7 @@ interface FilterPanelProps { setCurrentViewMode: (viewMode: ViewMode) => void; expandAllLabels: boolean; setExpandAllLabels: (expandAllLabels: boolean) => void; + hideViewModeOptions: boolean; } export function FilterPanel({ @@ -75,6 +76,7 @@ export function FilterPanel({ setCurrentViewMode, expandAllLabels, setExpandAllLabels, + hideViewModeOptions, }: FilterPanelProps) { const { sort, kinds } = params; @@ -118,34 +120,39 @@ export function FilterPanel({ /> - {BulkActions} - {currentViewMode === ViewMode.VIEW_MODE_LIST && ( - props.theme.colors.text.slightlyMuted}; - text-transform: none; - padding-left: ${props => props.theme.space[2]}px; - padding-right: ${props => props.theme.space[2]}px; - height: 22px; - `} - onClick={() => setExpandAllLabels(!expandAllLabels)} - > - - {expandAllLabels ? ( - - ) : ( - - )} - {expandAllLabels ? 'Collapse ' : 'Expand '} All Labels - - + {BulkActions} + {!hideViewModeOptions && ( + <> + {currentViewMode === ViewMode.VIEW_MODE_LIST && ( + props.theme.colors.text.slightlyMuted}; + text-transform: none; + padding-left: ${props => props.theme.space[2]}px; + padding-right: ${props => props.theme.space[2]}px; + height: 22px; + font-size: 12px; + `} + onClick={() => setExpandAllLabels(!expandAllLabels)} + > + + {expandAllLabels ? ( + + ) : ( + + )} + {expandAllLabels ? 'Collapse ' : 'Expand '} All Labels + + + )} + + )} - (); + const { setTrigger } = useInfiniteScroll({ fetch: fetchResources, }); const [selectedResources, setSelectedResources] = useState([]); + const [forceCardView, setForceCardView] = useState(false); const pinnedResourcesGetter = pinning.kind === 'supported' @@ -353,10 +361,27 @@ export function UnifiedResources(props: UnifiedResourcesProps) { unifiedResourcePreferences.labelsViewMode === LabelsViewMode.LABELS_VIEW_MODE_EXPANDED; + useEffect(() => { + const resizeObserver = new ResizeObserver(entries => { + const container = entries[0]; + if (container.contentRect.width <= FORCE_CARD_VIEW_BREAKPOINT) { + setForceCardView(true); + } else { + setForceCardView(false); + } + }); + + resizeObserver.observe(containerRef.current); + return () => { + resizeObserver.disconnect(); + }; + }, []); + const ViewComponent = - unifiedResourcePreferences.viewMode === ViewMode.VIEW_MODE_LIST - ? ListView - : CardsView; + unifiedResourcePreferences.viewMode === ViewMode.VIEW_MODE_CARD || + forceCardView + ? CardsView + : ListView; return (
{resourcesFetchAttempt.status === 'failed' && ( @@ -417,6 +444,7 @@ export function UnifiedResources(props: UnifiedResourcesProps) { : LabelsViewMode.LABELS_VIEW_MODE_COLLAPSED ); }} + hideViewModeOptions={forceCardView} BulkActions={ <> {selectedResources.length > 0 && ( @@ -430,9 +458,12 @@ export function UnifiedResources(props: UnifiedResourcesProps) { textTransform="none" onClick={() => action(getSelectedResources())} disabled={disabled} + size="small" css={` border: none; color: ${props => props.theme.colors.brand}; + height: 22px; + font-size: 12px; `} > diff --git a/web/packages/shared/components/UnifiedResources/unifiedStyles.css b/web/packages/shared/components/UnifiedResources/unifiedStyles.css index 67e72bbf99ef3..38ee89e7a99a2 100644 --- a/web/packages/shared/components/UnifiedResources/unifiedStyles.css +++ b/web/packages/shared/components/UnifiedResources/unifiedStyles.css @@ -37,7 +37,8 @@ .SearchPanel { width: 100%; - @container (min-width: 800px) { + @container (min-width: 801px) { width: 70%; + min-width: 800px; } } diff --git a/web/packages/teleport/src/Main/Main.tsx b/web/packages/teleport/src/Main/Main.tsx index 6ed2b5c478fde..4493d7921583a 100644 --- a/web/packages/teleport/src/Main/Main.tsx +++ b/web/packages/teleport/src/Main/Main.tsx @@ -20,6 +20,8 @@ import React, { useEffect, useMemo, useState, + createContext, + useContext, } from 'react'; import styled from 'styled-components'; import { Indicator } from 'design'; @@ -263,12 +265,34 @@ function FeatureRoutes({ lockedFeatures }: { lockedFeatures: LockedFeatures }) { return {routes}; } -export const ContentMinWidth = styled.div` - min-width: 1250px; - display: flex; - flex-direction: column; - flex: 1; -`; +// This context allows children components to disable this min-width in case they want to be able to shrink smaller. +type MinWidthContextState = { + setEnforceMinWidth: (enforceMinWidth: boolean) => void; +}; + +const ContentMinWidthContext = createContext(null); + +export const useContentMinWidthContext = () => + useContext(ContentMinWidthContext); + +const ContentMinWidth = ({ children }: { children: ReactNode }) => { + const [enforceMinWidth, setEnforceMinWidth] = useState(true); + + return ( + +
+ {children} +
+
+ ); +}; export const HorizontalSplit = styled.div` display: flex; diff --git a/web/packages/teleport/src/Main/index.ts b/web/packages/teleport/src/Main/index.ts index 3bd63809af3cf..23b76a41a01eb 100644 --- a/web/packages/teleport/src/Main/index.ts +++ b/web/packages/teleport/src/Main/index.ts @@ -16,7 +16,7 @@ limitations under the License. export { Main as default, - ContentMinWidth, + useContentMinWidthContext, HorizontalSplit, StyledIndicator, } from './Main'; diff --git a/web/packages/teleport/src/UnifiedResources/UnifiedResources.tsx b/web/packages/teleport/src/UnifiedResources/UnifiedResources.tsx index 048c7b9c3e0ca..e4da201ff9bac 100644 --- a/web/packages/teleport/src/UnifiedResources/UnifiedResources.tsx +++ b/web/packages/teleport/src/UnifiedResources/UnifiedResources.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useState, useEffect } from 'react'; import { Flex } from 'design'; @@ -38,6 +38,7 @@ import { FeatureHeaderTitle, FeatureBox, } from 'teleport/components/Layout'; +import { useContentMinWidthContext } from 'teleport/Main'; import AgentButtonAdd from 'teleport/components/AgentButtonAdd'; import { SearchResource } from 'teleport/Discover/SelectResource'; import { encodeUrlQueryParams } from 'teleport/components/hooks/useUrlFiltering'; @@ -99,6 +100,16 @@ function ClusterResources({ const teleCtx = useTeleport(); const flags = teleCtx.getFeatureFlags(); + const { setEnforceMinWidth } = useContentMinWidthContext(); + + useEffect(() => { + setEnforceMinWidth(false); + + return () => { + setEnforceMinWidth(true); + }; + }, []); + const pinningNotSupported = storageService.arePinnedResourcesDisabled(); const { getClusterPinnedResources,