Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update expandable component #6920

Merged
merged 5 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions website/docs/docs/cloud/dbt-cloud-ide/develop-in-the-cloud.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,21 +104,24 @@ Nice job, you're ready to start developing and building models 🎉!
- The IDE's idle session timeout is one hour.
- <Expandable alt_header="About the start up process and work retention">

### Start-up process
There are three start-up states when using or launching the Cloud IDE:
- **Creation start &mdash;** This is the state where you are starting the IDE for the first time. You can also view this as a *cold start* (see below), and you can expect this state to take longer because the git repository is being cloned.
- **Cold start &mdash;** This is the process of starting a new develop session, which will be available for you for one hour. The environment automatically turns off one hour after the last activity. This includes compile, preview, or any dbt invocation, however, it *does not* include editing and saving a file.
- **Hot start &mdash;** This is the state of resuming an existing or active develop session within one hour of the last activity.
The following sections describe the start-up process and work retention in the Cloud IDE.

- #### Start-up process
There are three start-up states when using or launching the Cloud IDE:
- **Creation start &mdash;** This is the state where you are starting the IDE for the first time. You can also view this as a *cold start* (see below), and you can expect this state to take longer because the git repository is being cloned.
- **Cold start &mdash;** This is the process of starting a new develop session, which will be available for you for one hour. The environment automatically turns off one hour after the last activity. This includes compile, preview, or any dbt invocation, however, it *does not* include editing and saving a file.
- **Hot start &mdash;** This is the state of resuming an existing or active develop session within one hour of the last activity. <br /><br />

- #### Work retention

### Work retention
The Cloud IDE needs explicit action to save your changes. There are three ways your work is stored:
The Cloud IDE needs explicit action to save your changes. There are three ways your work is stored:

- **Unsaved, local code &mdash;** The browser stores your code only in its local storage. In this state, you might need to commit any unsaved changes in order to switch branches or browsers. If you have saved and committed changes, you can access the "Change branch" option even if there are unsaved changes. But if you attempt to switch branches without saving changes, a warning message will appear, notifying you that you will lose any unsaved changes.
- **Unsaved, local code &mdash;** The browser stores your code only in its local storage. In this state, you might need to commit any unsaved changes in order to switch branches or browsers. If you have saved and committed changes, you can access the "Change branch" option even if there are unsaved changes. But if you attempt to switch branches without saving changes, a warning message will appear, notifying you that you will lose any unsaved changes.

<Lightbox src="/img/docs/dbt-cloud/cloud-ide/ide-unsaved-modal.jpg" width="85%" title="If you attempt to switch branches without saving changes, a warning message will appear, telling you that you will lose your changes."/>
<Lightbox src="/img/docs/dbt-cloud/cloud-ide/ide-unsaved-modal.jpg" width="85%" title="If you attempt to switch branches without saving changes, a warning message will appear, telling you that you will lose your changes."/>

- **Saved but uncommitted code &mdash;** When you save a file, the data gets stored in durable, long-term storage, but isn't synced back to git. To switch branches using the **Change branch** option, you must "Commit and sync" or "Revert" changes. Changing branches isn't available for saved-but-uncommitted code. This is to ensure your uncommitted changes don't get lost.
- **Committed code &mdash;** This is stored in the branch with your git provider and you can check out other (remote) branches.
- **Saved but uncommitted code &mdash;** When you save a file, the data gets stored in durable, long-term storage, but isn't synced back to git. To switch branches using the **Change branch** option, you must "Commit and sync" or "Revert" changes. Changing branches isn't available for saved-but-uncommitted code. This is to ensure your uncommitted changes don't get lost.
- **Committed code &mdash;** This is stored in the branch with your git provider and you can check out other (remote) branches.

</Expandable>

Expand Down
78 changes: 48 additions & 30 deletions website/src/components/expandable/index.js
Original file line number Diff line number Diff line change
@@ -1,55 +1,58 @@
/* eslint-disable */

import React, { useState, useEffect } from 'react';
/* eslint-disable */
import React, { useState, useEffect, useRef } from 'react';
import styles from './styles.module.css';
import Lifecycle from '../lifeCycle';

function slugify(text) {
return text.toString().toLowerCase()
.normalize('NFD') // normalize to nfd unicode form
.replace(/[\u0300-\u036f]/g, '') // remove diacritics
.replace(/\s+/g, '-') // replace spaces with -
.replace(/[^\w\-]+/g, '') // remove all non-word chars
.replace(/\-\-+/g, '-') // replace multiple - with a single -
.replace(/^-+/, '') // trim - from the start
.replace(/-+$/, ''); // trim - from the end
.normalize('NFD') // Normalize to NFD Unicode form
.replace(/[\u0300-\u036f]/g, '') // Remove diacritics
.replace(/\s+/g, '-') // Replace spaces with -
.replace(/[^\w\-]+/g, '') // Remove all non-word chars
.replace(/\-\-+/g, '-') // Replace multiple - with a single -
.replace(/^-+/, '') // Trim - from start
.replace(/-+$/, ''); // Trim - from end
}

function Expandable({ children, alt_header = null, lifecycle }) {
if (!alt_header) { return null; }
const [isOn, setOn] = useState(false);
if (!alt_header) return null;

const [isOpen, setIsOpen] = useState(false);
const detailsRef = useRef(null);
const anchorId = slugify(alt_header);

// Handles clicking on the header to expand/collapse
const handleToggleClick = (event) => {
event.preventDefault();
setOn(current => !current);
setIsOpen((prev) => !prev);
};

// Copy link function
const handleCopyClick = (event) => {
event.preventDefault();
event.stopPropagation();
const url = `${window.location.href.split('#')[0]}#${anchorId}`;
navigator.clipboard.writeText(url).then(() => {
showCopyPopup();
});
navigator.clipboard.writeText(url).then(() => showCopyPopup());
};

// Show "Link Copied!" popup
const showCopyPopup = () => {
const popup = document.createElement('div');
popup.classList.add('copy-popup');
popup.innerText = 'Link copied!';

// Add close button ('x')
// Close button ('x')
const closeButton = document.createElement('span');
closeButton.classList.add('close-button');
closeButton.innerHTML = ' &times;'; // '×' symbol for 'x'
closeButton.innerHTML = ' &times;';
closeButton.addEventListener('click', () => {
if (document.body.contains(popup)) {
document.body.removeChild(popup);
}
});
popup.appendChild(closeButton);

popup.appendChild(closeButton);
document.body.appendChild(popup);

setTimeout(() => {
Expand All @@ -59,20 +62,35 @@ function Expandable({ children, alt_header = null, lifecycle }) {
}, 3000);
};

// Auto-expand when linked via hash (URL fragment)
useEffect(() => {
if (window.location.hash === `#${anchorId}`) {
setOn(true);
const element = document.getElementById(anchorId);
if (element) {
element.scrollIntoView({ behavior: 'smooth' });
}
setIsOpen(true);
detailsRef.current?.scrollIntoView({ behavior: 'smooth' });
}
}, [anchorId]);

// Observe search highlight and auto-expand
useEffect(() => {
const observer = new MutationObserver(() => {
const details = detailsRef.current;
if (details && details.querySelector('mark')) {
details.open = true;
}
});

observer.observe(document.body, {
childList: true,
subtree: true,
});

return () => observer.disconnect();
}, []);

return (
<div id={anchorId} className={`${styles.expandableContainer}`}>
<div className={styles.header} onClick={handleToggleClick}>
<span className={`${styles.toggle} ${isOn ? styles.toggleDown : styles.toggleRight}`}></span>
<details ref={detailsRef} id={anchorId} className={styles.expandableContainer} open={isOpen}>
<summary className={styles.header} onClick={handleToggleClick}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work setting the open attribute programmatically here.

Once small adjustment we need to make is adjusting the CSS for the chevron when ctrl + f searching.

You'll see in the screenshot even though the expandable is open, the chevron still is in the closed state.
About_the_dbt_Cloud_IDE___dbt_Developer_Hub

We should be able to clean this up by reading the open attribute within the CSS.

For example,
details[open] .toggle {transform: rotate(225deg);}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh great catch! thank you so much @john-rock ! ok updated it now and used ::before since the chevron arrow is rendered through a pseudo-element.

<span className={`${styles.toggle} ${isOpen ? styles.toggleDown : styles.toggleRight}`}></span>
&nbsp;
<span className={styles.headerText}>
{alt_header}
Expand All @@ -81,11 +99,11 @@ function Expandable({ children, alt_header = null, lifecycle }) {
</span>
</span>
<span onClick={handleCopyClick} className={styles.copyIcon}></span>
</div>
<div style={{ display: isOn ? 'block' : 'none' }} className={styles.body}>
</summary>
<div className={styles.body}>
{children}
</div>
</div>
</details>
);
}

Expand Down
5 changes: 2 additions & 3 deletions website/src/components/expandable/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@
}

:local(html[data-theme='dark'] details .headerText) {
color: rgba(18, 12, 12, 0.862); /* this adds black text inside details in dark mode */
}
color: rgba(255, 255, 255, 0.9); /* Change to white text in dark mode */
}

:local(.body > p:last-child) {
margin-bottom: 0px;
Expand Down Expand Up @@ -146,4 +146,3 @@
display: flex;
align-items: center;
}

Loading