Skip to content

Commit

Permalink
Aspects Page: Expand/Collapse nested objects + Copy JSON (#6563)
Browse files Browse the repository at this point in the history
* allow expand/collapse for nested config/data in aspect page

* add copy button

* style fixea

* change hover timeout

* refactor

Co-authored-by: Luv Kapur <[email protected]>
  • Loading branch information
luvkapur and luvkapur authored Oct 19, 2022
1 parent c1efaca commit 85cee56
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 17 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,6 @@ package-lock.json
!.yarn/plugins
!.yarn/sdks
!.yarn/versions

# component issues
scopes/component/component-issues/dist/
42 changes: 42 additions & 0 deletions scopes/harmony/ui/aspect-box/aspect-box.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,49 @@
cursor: pointer;
color: var(--bit-text-color-light, #6c707c);
}
.sectionTitleContainer {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.expandCollapse {
display: flex;
align-items: flex-start;
cursor: pointer;

img {
border-radius: 8px;
width: 16px;
&:hover {
background-color: var(--bit-border-color-lightest, #ededed);
}
}
}
.copyMessage {
position: unset;
transform: unset;
display: flex;
}

.toolbar {
flex-direction: row;
display: flex;
align-items: flex-start;
}
.copyIcon {
display: flex;
font-size: var(--bit-p-xs);
}
.copyButton {
cursor: pointer;
box-sizing: border-box;
border: none;
background-color: var(--bit-bg-color-highlight);
color: var(--bit-accent-color);
outline: none;
}
.sectionTitle {
display: flex;
margin-bottom: 16px;
font-weight: bold;
font-size: var(--bit-p-xs);
Expand Down
83 changes: 66 additions & 17 deletions scopes/harmony/ui/aspect-box/aspect-box.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import classNames from 'classnames';
import React from 'react';
import React, { useState } from 'react';
import JSONFormatter from 'json-formatter-js';
import { isObject } from 'lodash';
import { CopiedMessage } from '@teambit/documenter.ui.copied-message';
import { Icon } from '@teambit/evangelist.elements.icon';
import copy from 'copy-to-clipboard';

import styles from './aspect-box.module.scss';

export type AspectBoxProps = {
Expand All @@ -11,12 +16,10 @@ export type AspectBoxProps = {
data: any;
} & React.HTMLAttributes<HTMLDivElement>;

const collapsedIcon = 'https://static.bit.dev/bit-icons/collapse.svg';
const expandIcon = 'https://static.bit.dev/bit-icons/expand.svg';

export function AspectBox({ icon, name, config, data, className, ...rest }: AspectBoxProps) {
const configContent = new JSONFormatter(config, 1, {
theme: 'dark',
hoverPreviewEnabled: true,
});
const dataContent = new JSONFormatter(data, 1, { theme: 'dark' });
return (
<div {...rest} className={classNames(styles.aspectBox, className)}>
<div className={styles.titleLine}>
Expand All @@ -30,22 +33,68 @@ export function AspectBox({ icon, name, config, data, className, ...rest }: Aspe
<Icon of="open-tab" />
</a> */}
</div>
<div className={styles.sectionTitle}>Configuration</div>
<div className={classNames(styles.log, styles.config)}>
<div
ref={(nodeElement) => {
nodeElement && nodeElement.appendChild(configContent.render());
}}
/>
<DisplayJsonTree title={'Configuration'} object={config} />
<DisplayJsonTree title={'Data'} object={data} />
</div>
);
}

/**
* @todo extract to a separate component
*/

function DisplayJsonTree({ object, title }: { object: any; title: string }) {
const [expandedDepth, setExpandedDepth] = useState<number>(1);
const [isCopied, setIsCopied] = useState(false);
const isDepthGreaterThanOne = Object.keys(object).some((key) => isObject(object[key]));
const jsonContent = new JSONFormatter(object, expandedDepth, {
theme: 'dark',
hoverPreviewEnabled: true,
});
const collapsedExpandedIcon = expandedDepth === 1 ? expandIcon : collapsedIcon;

const handleClick = (dataToCopy) => () => {
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, 2000);
copy(JSON.stringify(dataToCopy, null, 2));
};

const toggleExpandedDepth = (state: number) => {
if (state === 1) return Infinity;
return 1;
};

return (
<>
<div className={styles.sectionTitleContainer}>
<div className={styles.sectionTitle}>{title}</div>
<div className={styles.toolbar}>
<CopiedMessage className={styles.copyMessage} show={isCopied} />
<div className={styles.copy}>
<button className={styles.copyButton} onClick={handleClick(jsonContent.json)}>
<Icon className={styles.copyIcon} of="copy-cmp" />
</button>
</div>

{isDepthGreaterThanOne && (
<div className={styles.expandCollapse}>
<img
src={collapsedExpandedIcon}
onClick={() => setExpandedDepth((value) => toggleExpandedDepth(value))}
/>
</div>
)}
</div>
</div>
<div className={styles.sectionTitle}>Calculated data</div>
<div className={styles.log}>
<div className={classNames(styles.log, styles.config)}>
<div
ref={(nodeElement) => {
nodeElement && nodeElement.appendChild(dataContent.render());
nodeElement && nodeElement.replaceChildren(jsonContent.render());
}}
/>
</div>
</div>
</>
);
}

0 comments on commit 85cee56

Please sign in to comment.