Skip to content

Commit

Permalink
Merge pull request #1348 from kubeshop/devcatalin/feat/navigator-char…
Browse files Browse the repository at this point in the history
…t-file

show Chart.yaml, improve helm pane styling and section blueprint functionality
  • Loading branch information
devcatalin authored Feb 14, 2022
2 parents 247f007 + abafe4c commit bd6a08a
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ export type ItemRendererProps<ItemType, ScopeType> = {
isLastItem: boolean;
isSectionCheckable: boolean;
sectionContainerElementId: string;
indentation: number;
options?: ItemRendererOptions;
};

function ItemRenderer<ItemType, ScopeType>(props: ItemRendererProps<ItemType, ScopeType>) {
const {itemId, blueprint, level, isLastItem, isSectionCheckable, sectionContainerElementId, options} = props;
const {itemId, blueprint, level, isLastItem, isSectionCheckable, sectionContainerElementId, indentation, options} =
props;
const {instanceHandler} = blueprint;

const dispatch = useAppDispatch();
Expand Down Expand Up @@ -80,6 +82,7 @@ function ItemRenderer<ItemType, ScopeType>(props: ItemRendererProps<ItemType, Sc
level={level}
isLastItem={isLastItem}
hasOnClick={Boolean(instanceHandler?.onClick)}
$indentation={indentation}
$isSectionCheckable={isSectionCheckable}
$hasCustomNameDisplay={Boolean(NameDisplay.Component)}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type ItemContainerProps = {
level: number;
isLastItem: boolean;
hasOnClick: boolean;
$indentation: number;
$isSectionCheckable: boolean;
$hasCustomNameDisplay: boolean;
};
Expand All @@ -21,7 +22,10 @@ export const ItemContainer = styled.span<ItemContainerProps>`
align-items: center;
width: 100%;
user-select: none;
padding-left: ${props => (props.$isSectionCheckable ? '32px;' : '26px;')}
${props => {
const defaultIndentation = props.$isSectionCheckable ? 32 : 26;
return `padding-left: ${defaultIndentation + props.$indentation}px;`;
}}
padding-right: 8px;
margin-bottom: 2px;
${props => props.hasOnClick && `cursor: pointer;`}
Expand Down
16 changes: 14 additions & 2 deletions src/components/molecules/SectionRenderer/SectionHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,23 @@ interface SectionHeaderProps {
isCollapsed: boolean;
isLastSection: boolean;
level: number;
indentation: number;
expandSection: () => void;
collapseSection: () => void;
}

function SectionHeader(props: SectionHeaderProps) {
const {name, sectionInstance, sectionBlueprint, isCollapsed, isLastSection, level, expandSection, collapseSection} =
props;
const {
name,
sectionInstance,
sectionBlueprint,
isCollapsed,
isLastSection,
level,
indentation,
expandSection,
collapseSection,
} = props;
const dispatch = useAppDispatch();
const [isHovered, setIsHovered] = useState<boolean>(false);

Expand Down Expand Up @@ -82,6 +92,7 @@ function SectionHeader(props: SectionHeaderProps) {
isHovered={isHovered}
isCheckable={Boolean(sectionBlueprint.builder?.makeCheckable)}
$hasCustomNameDisplay={Boolean(NameDisplay.Component)}
$indentation={indentation}
>
{sectionInstance.checkable &&
sectionInstance.isInitialized &&
Expand All @@ -107,6 +118,7 @@ function SectionHeader(props: SectionHeaderProps) {
$isSelected={sectionInstance.isSelected && isCollapsed}
$isHighlighted={sectionInstance.isSelected && isCollapsed}
$isCheckable={Boolean(sectionInstance.checkable)}
$nameColor={sectionBlueprint.customization?.nameColor}
$level={level}
onClick={toggleCollapse}
>
Expand Down
26 changes: 24 additions & 2 deletions src/components/molecules/SectionRenderer/SectionRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ type SectionRendererProps = {
sectionBlueprint: SectionBlueprint<any>;
level: number;
isLastSection: boolean;
parentIndentation?: number;
itemRendererOptions?: ItemRendererOptions;
};

function SectionRenderer(props: SectionRendererProps) {
const {sectionBlueprint, level, isLastSection, itemRendererOptions} = props;
const {sectionBlueprint, level, isLastSection, itemRendererOptions, parentIndentation} = props;

const {itemBlueprint, name: sectionName, id: sectionId} = sectionBlueprint;

Expand All @@ -46,6 +47,23 @@ function SectionRenderer(props: SectionRendererProps) {

const collapsedSectionIds = useAppSelector(state => state.navigator.collapsedSectionIds);

const sectionIndentation = useMemo(() => {
const indentation = sectionBlueprint.customization?.indentation;
if (!parentIndentation && !indentation) {
return undefined;
}
if (parentIndentation && !indentation) {
return parentIndentation;
}
if (!parentIndentation && indentation) {
return indentation;
}
if (parentIndentation && indentation) {
return parentIndentation + indentation;
}
return undefined;
}, [parentIndentation, sectionBlueprint.customization?.indentation]);

const isCollapsedMode = useMemo(() => {
if (!sectionInstance?.id) {
return 'expanded';
Expand Down Expand Up @@ -188,6 +206,7 @@ function SectionRenderer(props: SectionRendererProps) {
level={level}
expandSection={expandSection}
collapseSection={collapseSection}
indentation={sectionIndentation || 0}
/>
{sectionInstance &&
sectionInstance.isVisible &&
Expand All @@ -200,10 +219,11 @@ function SectionRenderer(props: SectionRendererProps) {
itemId={itemId}
blueprint={itemBlueprint}
level={level + 1}
isLastItem={isLastVisibleItemId(itemId)}
isLastItem={isLastVisibleItemId(itemId) && !childSectionIds}
isSectionCheckable={Boolean(sectionInstance.checkable)}
sectionContainerElementId={sectionBlueprint.containerElementId}
options={itemRendererOptions}
indentation={sectionIndentation || 0}
/>
))}
{sectionInstance?.isVisible &&
Expand Down Expand Up @@ -231,6 +251,7 @@ function SectionRenderer(props: SectionRendererProps) {
isSectionCheckable={Boolean(sectionInstance.checkable)}
sectionContainerElementId={sectionBlueprint.containerElementId}
options={itemRendererOptions}
indentation={sectionIndentation || 0}
/>
))
) : (
Expand All @@ -248,6 +269,7 @@ function SectionRenderer(props: SectionRendererProps) {
sectionBlueprint={child}
level={level + 1}
isLastSection={child.id === lastVisibleChildSectionId}
parentIndentation={sectionIndentation}
/>
))}
</>
Expand Down
11 changes: 8 additions & 3 deletions src/components/molecules/SectionRenderer/styled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import styled from 'styled-components';
import Colors, {FontColors} from '@styles/Colors';

type NameContainerProps = {
$hasCustomNameDisplay: boolean;
$indentation: number;
isHovered?: boolean;
isCheckable?: boolean;
$hasCustomNameDisplay: boolean;
};

type SectionContainerProps = {
Expand All @@ -28,7 +29,10 @@ export const NameContainer = styled.span<NameContainerProps>`
display: flex;
align-items: center;
width: 100%;
${props => props.isCheckable && `padding-left: 24px;`}
${props => {
const defaultIndentation = props.isCheckable ? 24 : 0;
return `padding-left: ${defaultIndentation + props.$indentation}px;`;
}}
${props => !props.isHovered && 'padding-right: 30px;'}
${props => props.$hasCustomNameDisplay && 'padding: 0;'}
`;
Expand Down Expand Up @@ -80,6 +84,7 @@ type NameProps = {
$isHighlighted?: boolean;
$isCheckable?: boolean;
$level: number;
$nameColor?: string;
};

export const Name = styled.span<NameProps>`
Expand All @@ -104,7 +109,7 @@ export const Name = styled.span<NameProps>`
if (props.$isSelected) {
return `color: ${Colors.blackPure};`;
}
return `color: ${Colors.whitePure};`;
return props.$nameColor ? `color: ${props.$nameColor}` : `color: ${Colors.whitePure};`;
}}
`;

Expand Down
11 changes: 2 additions & 9 deletions src/components/organisms/PaneManager/MenuButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,6 @@ const MenuButton: React.FC<IMenuButtonProps> = props => {
shallowEqual
);

const isAnyHelmValuesFileSelected = useMemo(() => {
return Object.values(helmValuesMap).some(v => v.isSelected);
}, [helmValuesMap]);

const isAnySectionSelected = useMemo(() => {
if (!sectionInstanceByName) {
return false;
Expand All @@ -69,11 +65,8 @@ const MenuButton: React.FC<IMenuButtonProps> = props => {
const style: React.CSSProperties = {};

const hasGradientBackground = useMemo(() => {
return Boolean(
(isAnySectionSelected || (shouldWatchSelectedPath && selectedPath && !isAnyHelmValuesFileSelected)) &&
(!isSelected || !isActive)
);
}, [isAnySectionSelected, shouldWatchSelectedPath, selectedPath, isAnyHelmValuesFileSelected, isSelected, isActive]);
return Boolean((isAnySectionSelected || (shouldWatchSelectedPath && selectedPath)) && (!isSelected || !isActive));
}, [isAnySectionSelected, shouldWatchSelectedPath, selectedPath, isSelected, isActive]);

if (hasGradientBackground) {
if (isHovered) {
Expand Down
3 changes: 3 additions & 0 deletions src/models/navigator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ export interface SectionCustomization {
};
/** If no value is provided, default value will be "descendants" */
counterDisplayMode?: 'descendants' | 'items' | 'subsections' | 'none';
/** Number of pixels to indent this section, by default all sections/susections are aligned */
indentation?: number;
nameColor?: string;
emptyGroupText?: string;
disableHoverStyle?: boolean;
beforeInitializationText?: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import {HELM_CHART_SECTION_NAME, ROOT_FILE_ENTRY} from '@constants/constants';

import {HelmValuesMapType} from '@models/appstate';
import {HelmChartMapType, HelmValuesMapType} from '@models/appstate';
import {HelmChart, HelmValuesFile} from '@models/helm';
import {SectionBlueprint} from '@models/navigator';

import {selectHelmValuesFile} from '@redux/reducers/main';
import {selectFile, selectHelmValuesFile} from '@redux/reducers/main';

import Colors from '@styles/Colors';

import sectionBlueprintMap from '../sectionBlueprintMap';
import HelmChartQuickAction from './HelmChartQuickAction';
Expand All @@ -16,6 +18,13 @@ export type ValuesFilesScopeType = {
isFolderOpen: boolean;
};

type HelmChartScopeType = {
helmChartMap: HelmChartMapType;
selectedPath: string | undefined;
previewValuesFileId: string | undefined;
isInClusterMode: boolean;
};

export function makeHelmChartSectionBlueprint(helmChart: HelmChart) {
const valuesFilesSectionBlueprint: SectionBlueprint<HelmValuesFile, ValuesFilesScopeType> = {
name: 'Values Files',
Expand Down Expand Up @@ -48,6 +57,8 @@ export function makeHelmChartSectionBlueprint(helmChart: HelmChart) {
},
customization: {
counterDisplayMode: 'items',
indentation: 8,
nameColor: Colors.grey400,
},
itemBlueprint: {
getName: rawItem => rawItem.name,
Expand All @@ -73,17 +84,55 @@ export function makeHelmChartSectionBlueprint(helmChart: HelmChart) {
},
};

const helmChartSectionBlueprint: SectionBlueprint<any, any> = {
const helmChartSectionBlueprint: SectionBlueprint<HelmChart, HelmChartScopeType> = {
name: helmChart.name,
id: helmChart.id,
containerElementId: 'helm-sections-container',
rootSectionId: HELM_CHART_SECTION_NAME,
childSectionIds: [valuesFilesSectionBlueprint.id],
getScope: () => {
return {};
getScope: state => {
const kubeConfigPath = state.config.projectConfig?.kubeConfig?.path || state.config.kubeConfig.path;
return {
helmChartMap: state.main.helmChartMap,
isInClusterMode: kubeConfigPath
? Boolean(state.main.previewResourceId && state.main.previewResourceId.endsWith(kubeConfigPath))
: false,
previewValuesFileId: state.main.previewValuesFileId,
selectedPath: state.main.selectedPath,
};
},
builder: {
getRawItems: scope => {
const chart: HelmChart | undefined = scope.helmChartMap[helmChart.id];
return chart ? [chart] : [];
},
},
itemBlueprint: {
getName: () => 'Chart.yaml',
getInstanceId: chart => chart.id,
builder: {
getMeta: chart => ({
filePath: chart.filePath,
}),
isSelected: (chart, scope) => {
return scope.selectedPath === chart.filePath;
},
isDisabled: (rawItem, scope) =>
Boolean((scope.previewValuesFileId && scope.previewValuesFileId !== rawItem.id) || scope.isInClusterMode),
},
instanceHandler: {
onClick: (instance, dispatch) => {
const filePath: string | undefined = instance.meta?.filePath;
if (!filePath) {
return;
}
dispatch(selectFile({filePath}));
},
},
},
customization: {
counterDisplayMode: 'none',
indentation: 8,
},
};

Expand Down

0 comments on commit bd6a08a

Please sign in to comment.