Skip to content
1 change: 1 addition & 0 deletions .cursor/rules/django_javascript_implementation.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ alwaysApply: true
- HTML fragments that may change throughout the page lifecycle should be created as django template files under `components/`
- Any variables within those components should be handled in the Javascript handlers/managers and passed as context objects to the Django view `RenderHTMLFragmentView` which renders the Django template files in `components/`using a context object passed into a Javascript `APIClient` call.
- Never try to render HTML fragments in Javascript.
- Where appropriate, use programmatic DOM construction through the JS DOM APIs instead of HTML template literals in Javascript. All user defined inputs should be passed to the DOM as text strings (e.g. using `textContent`, `document.createTextNode()`, `String()` when setting user defined values/attributes/text).
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,7 @@ def test_get_dataset_files_missing_uuid(self):
assert response.status_code == status.HTTP_404_NOT_FOUND

def test_get_dataset_files_unauthenticated_access(self):
"""Test access without API key or session."""
"""Test access without API key (no forced user on test client)."""
url = reverse("api:datasets-files", kwargs={"pk": self.dataset.uuid})
unauthenticated_client = APIClient()
response = unauthenticated_client.get(url)
Expand Down
13 changes: 1 addition & 12 deletions gateway/sds_gateway/static/css/components.css
Original file line number Diff line number Diff line change
Expand Up @@ -1127,21 +1127,10 @@ body {
border-radius: 0.375rem;
}

#file-tree-table {
#file-tree-root {
margin-bottom: 0;
}

#file-tree-table thead.sticky-top {
position: sticky;
top: 0;
z-index: 2;
background-color: var(--bs-white);
}

#file-tree-table tbody tr:hover {
cursor: pointer;
}

.action-buttons {
display: flex;
gap: 0.5rem;
Expand Down
111 changes: 111 additions & 0 deletions gateway/sds_gateway/static/css/spectrumx_theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,116 @@ textarea:focus-visible {
max-width: 600px;
}

.file-browser-modal {
max-width: none;
width: 100%;
}

/* Modal file picker: compact rows, hidden checkboxes, click-to-select */
.file-browser-modal #file-tree-root {
padding-left: 0;
}

.file-browser-modal .file-browser-row {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 0.5rem;
padding: 0.375rem 0.75rem;
border-radius: 6px;
cursor: pointer;
transition: background-color 0.15s ease, color 0.15s ease;
width: 100%;
box-sizing: border-box;
}

.file-browser-modal [role="treeitem"]:focus-visible {
outline: 2px solid #005a9c;
outline-offset: 2px;
}

.file-browser-modal .file-item > .file-browser-row:hover {
background-color: rgba(0, 90, 156, 0.08);
}

.file-browser-modal .file-item.is-selected > .file-browser-row {
background-color: rgba(0, 90, 156, 0.16);
color: #003d6b;
font-weight: 500;
}

.file-browser-modal .file-item.is-selected > .file-browser-row .bi {
color: #005a9c;
}

.file-browser-modal .folder-item > .file-browser-row:hover {
background-color: #e9ecef;
}

.file-browser-modal .folder-item.is-selected > .file-browser-row {
background-color: rgba(0, 90, 156, 0.16);
color: #003d6b;
font-weight: 500;
}

.file-browser-modal .folder-expand-toggle {
display: none;
flex-shrink: 0;
line-height: 1;
color: inherit;
text-decoration: none;
}

.file-browser-modal.folder-selection-mode-active .folder-expand-toggle {
display: inline-flex;
align-items: center;
justify-content: center;
}

.file-browser-modal .folder-expand-icon {
transition: transform 0.15s ease;
}

.file-browser-modal .folder-expand-icon.folder-expand-icon-open {
transform: rotate(90deg);
}

.file-browser-modal .file-item.readonly-row > .file-browser-row {
cursor: not-allowed;
opacity: 0.65;
}

.file-browser-modal .file-item.readonly-row > .file-browser-row:hover {
background-color: transparent;
}

.file-browser-modal .item-content {
display: flex;
align-items: center;
gap: 0.5rem;
flex: 1;
min-width: 0;
justify-content: flex-start;
}

.file-browser-modal .file-browser-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

.file-browser-modal .file-checkbox {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}

.file-browser-header {
display: flex;
justify-content: space-between;
Expand Down Expand Up @@ -450,6 +560,7 @@ textarea:focus-visible {
}

/* CSS for folder expansion/collapse */
.file-browser li[aria-expanded="true"] > ul,
.file-browser [aria-expanded="true"] ~ ul {
display: block;
}
Expand Down
2 changes: 1 addition & 1 deletion gateway/sds_gateway/static/js/core/PageLifecycleManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ class PageLifecycleManager {
searchFormId: "files-search-form",
searchButtonId: "search-files",
clearButtonId: "clear-files-search",
tableBodyId: "file-tree-table",
tableBodyId: "file-tree-root",
paginationContainerId: "files-pagination",
type: "files",
formHandler: this.datasetModeManager?.getHandler(),
Expand Down
4 changes: 4 additions & 0 deletions gateway/sds_gateway/static/js/dataset/AuthorsManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,10 @@ class AuthorsManager {
static bindFileTreeModalHandlers(handler) {
const modal = document.getElementById("fileTreeModal")
if (!modal) return
if (modal.dataset.fileTreeModalHandlersBound === "true") {
return
}
modal.dataset.fileTreeModalHandlersBound = "true"
modal.addEventListener("show.bs.modal", () => {
handler.onFileModalShow()
})
Expand Down
Loading
Loading