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

fix: selector indicator for dataset scope selection #238

Merged
merged 1 commit into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 3 additions & 3 deletions src/js/components/BentoAppRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ const ScopedRoute = () => {
const { projectId, datasetId } = useParams();
const dispatch = useAppDispatch();
const navigate = useNavigate();
const { selectedScope, projects, projectsStatus } = useMetadata();
const { selectedScope, projectsByID, projectsStatus } = useMetadata();

useEffect(() => {
if (WAITING_STATES.includes(projectsStatus)) return; // Wait for projects to load first

// Update selectedScope based on URL parameters
const valid = validProjectDataset(projects, { project: projectId, dataset: datasetId });
const valid = validProjectDataset(projectsByID, { project: projectId, dataset: datasetId });

// Don't change the scope object if the scope value is the same, otherwise it'll trigger needless re-renders.
if (scopeEqual(selectedScope.scope, valid.scope)) {
Expand Down Expand Up @@ -72,7 +72,7 @@ const ScopedRoute = () => {
}
const newPathString = '/' + newPath.join('/');
navigate(newPathString, { replace: true });
}, [projects, projectsStatus, projectId, datasetId, dispatch, navigate, selectedScope]);
}, [projectsByID, projectsStatus, projectId, datasetId, dispatch, navigate, selectedScope]);

return <Outlet />;
};
Expand Down
9 changes: 8 additions & 1 deletion src/js/components/Scope/DatasetScopePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,14 @@ const DatasetScopePicker = ({ parentProject }: DatasetScopePickerProps) => {
<List
dataSource={parentProject.datasets}
bordered
renderItem={(d) => <Dataset parentProjectID={parentProject.identifier} dataset={d} format="list-item" />}
renderItem={(d) => (
<Dataset
parentProjectID={parentProject.identifier}
dataset={d}
format="list-item"
selected={scopeObj.dataset === d.identifier}
/>
)}
/>
</Space>
);
Expand Down
8 changes: 6 additions & 2 deletions src/js/features/metadata/metadata.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ export type DiscoveryScopeSelection = {

export interface MetadataState {
projects: Project[];
projectsByID: Record<string, Project>;
projectsStatus: RequestStatus;
selectedScope: DiscoveryScopeSelection;
}

const initialState: MetadataState = {
projects: [],
projectsByID: {},
projectsStatus: RequestStatus.Idle,
selectedScope: {
scope: { project: undefined, dataset: undefined },
Expand Down Expand Up @@ -66,7 +68,7 @@ const metadata = createSlice({
// Defaults to the narrowest possible scope if there is only 1 project and only 1 dataset.
// This forces Katsu to resolve the Discovery config with fallbacks from the bottom-up:
// dataset -> project -> whole node
state.selectedScope = validProjectDataset(state.projects, payload);
state.selectedScope = validProjectDataset(state.projectsByID, payload);
},
markScopeSet: (state) => {
state.selectedScope.scopeSet = true;
Expand All @@ -77,7 +79,9 @@ const metadata = createSlice({
state.projectsStatus = RequestStatus.Pending;
});
builder.addCase(getProjects.fulfilled, (state, { payload }) => {
state.projects = payload?.results ?? [];
const projects = payload?.results ?? [];
state.projects = projects;
state.projectsByID = Object.fromEntries(projects.map((p) => [p.identifier, p]));
state.projectsStatus = RequestStatus.Fulfilled;
});
builder.addCase(getProjects.rejected, (state) => {
Expand Down
26 changes: 15 additions & 11 deletions src/js/utils/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ export const getCurrentPage = (): string => {
}
};

export const validProjectDataset = (projects: Project[], unvalidatedScope: DiscoveryScope): DiscoveryScopeSelection => {
export const validProjectDataset = (
projectsByID: Record<string, Project>,
unvalidatedScope: DiscoveryScope
): DiscoveryScopeSelection => {
const { project, dataset } = unvalidatedScope;

const valid: DiscoveryScopeSelection = {
Expand All @@ -22,29 +25,30 @@ export const validProjectDataset = (projects: Project[], unvalidatedScope: Disco
fixedDataset: false,
};

const projects = Object.values(projectsByID);

if (projects.length === 1) {
// automatic project scoping if only 1
// Automatic project scoping if only 1
// - if there is only one project, it should be auto-selected, since it contains the same set of data as the node.
const defaultProj = projects[0];
valid.scope.project = defaultProj.identifier;
valid.fixedProject = true;
if (defaultProj.datasets.length === 1) {
// TODO: only if the dataset-level permissions equal the project-level ones...
// automatic dataset scoping if only 1
valid.scope.dataset = defaultProj.datasets[0].identifier;
valid.fixedDataset = true;
// early return to ignore redundant projectId and datasetId
return valid;
}
}
if (project && projects.find(({ identifier }) => identifier === project)) {

const selectedProject: Project | undefined = project ? projectsByID[project] : undefined;

if (project && selectedProject) {
valid.scope.project = project;
if (dataset) {
if (
projects
.find(({ identifier }) => identifier === project)!
.datasets.find(({ identifier }) => identifier === dataset)
) {
valid.scope.dataset = dataset;
}
if (dataset && selectedProject.datasets.find(({ identifier }) => identifier === dataset)) {
valid.scope.dataset = dataset;
}
}
return valid;
Expand Down
Loading