Skip to content

Commit

Permalink
feat: LEAP-1020: Relation side panel improvements (#5711)
Browse files Browse the repository at this point in the history
<!-- feat: Relations side panel improvements -->
## Overview
This PR implements improvements to the relations list based on the
accepted [prd: Relations list improvements]
See #5689 #5447 #5446 



https://github.com/HumanSignal/label-studio/assets/87583799/ec5276b1-b940-4d55-ac54-546380a6ef0b




## Changes

### Relations list ordering
- Added ordering options to relations list sidepanel 
- Ordering by creation time (ascending or descending)
- Ordering preference persists between sessions

### Relations visibility toggle  
- Added toggle icon next to "Relations" label
- Toggle hides all relations when one or more are visible
- Toggle shows all relations when all are hidden
- Visibility state persists between sessions

## Testing
- Added unit tests for ordering and visibility functionality
- Updated documentation with usage instructions

## PR Requirements 

- [x] Commit message(s) and PR title follows the format 
- [x] Tests for the changes have been added/updated
- [x] Docs have been added/updated  
- [x] Best efforts for concise/coherent code

#### Areas Impacted
- [ ] Product design
- [ ] Backend (Database) 
- [ ] Backend (API)
- [x] Frontend

### Change Details
This is a new feature implementing relations list ordering and
visibility toggling based on the provided requirements.

No libraries were added or updated.

This change should have a positive performance impact by allowing users
to easily focus on relevant relations.

This change does not impact security.

Alternatives were manually hiding relations and scrolling down which is
tedious.

No feature flags were used.

### Breaking Change
- [ ] Yes, and covered entirely by feature flags
- [ ] Yes, and covered partially by feature flags  
- [x] No
- [ ] Not sure

### Testing 
- [ ] e2e
- [x] integration
- [ ] unit

### Affected Domains
- Relations Details Panel

## Acceptance Criteria
- [x] Adds options to order relations list by creation time (ascending
and descending)
- [x] Persists ordering preference between sessions 
- [x] Integration test for ordering functionality

- [x] Adds a toggle icon next to "Relations" label
- [x] Toggle hides all relations when one or more are visible
- [x] Toggle shows all relations when all are hidden
- [x] Persists visibility state between sessions 
- [x] Integration tests for visibility functionality

Closes #5689 #5447 #5446

---------

Co-authored-by: bmartel <[email protected]>
  • Loading branch information
Precious-Macaulay and bmartel authored Jun 14, 2024
1 parent fb5e222 commit 9df4eb9
Show file tree
Hide file tree
Showing 9 changed files with 590 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,17 @@

.history
&__section-tab
padding 8px
padding 8px

.relations
&__view-control
display grid
grid-auto-flow column
grid-auto-columns min-content
align-items center
grid-column-gap 4px
text-wrap nowrap
padding 4px
font-family "Roboto", Arial, sans-serif
font-weight 500
font-size 15px
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Relations as RelationsComponent } from "./Relations";
// eslint-disable-next-line
// @ts-ignore
import { DraftPanel } from "../../DraftPanel/DraftPanel";
import { RelationsControls } from "./RelationsControls";
interface DetailsPanelProps extends PanelProps {
regions: any;
selection: any;
Expand Down Expand Up @@ -71,7 +72,10 @@ const RelationsTab: FC<any> = inject("store")(
<>
<Block name="relations">
<Elem name="section-tab">
<Elem name="section-head">Relations ({relationStore.size})</Elem>
<Elem name="view-control">
<Elem name="section-head">Relations ({relationStore.size})</Elem>
<RelationsControls relationStore={relationStore} />
</Elem>
<Elem name="section-content">
<RelationsComponent relationStore={relationStore} />
</Elem>
Expand Down Expand Up @@ -146,7 +150,10 @@ const GeneralPanel: FC<any> = inject("store")(
</Elem>
)}
<Elem name="section">
<Elem name="section-head">Relations ({relationStore.size})</Elem>
<Elem name="view-control">
<Elem name="section-head">Relations ({relationStore.size})</Elem>
<RelationsControls relationStore={relationStore} />
</Elem>
<Elem name="section-content">
<RelationsComponent relationStore={relationStore} />
</Elem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import { Select } from "antd";
import "./Relations.styl";

const RealtionsComponent: FC<any> = ({ relationStore }) => {
const relations = relationStore.orderedRelations;

return (
<Block name="relations">
<RelationsList relations={relationStore.relations} />
<RelationsList relations={relations} />
</Block>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
.relation-controls
display flex
align-items center

.button
height 24px
font-size 14px
font-weight 400
padding 0px 2px !important
justify-content flex-start

&__icon
width 24px
height 24px
flex none
margin 0

&:focus
box-shadow none

&:hover
background rgba(65, 60, 74, 0.08)
border-radius 4px

&[disabled]
background-color #fff
color rgba(#000, 0.3)
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React, { type FC, useCallback } from "react";

Check notice on line 1 in web/libs/editor/src/components/SidePanels/DetailsPanel/RelationsControls.tsx

View workflow job for this annotation

GitHub Actions / merge

Merge Conflict automatically resolved web/libs/editor/src/components/SidePanels/DetailsPanel/RelationsControls.tsx
import { observer } from "mobx-react";
import { Block, Elem } from "../../../utils/bem";
import { Button } from "../../../common/Button/Button";
import "./RelationsControls.styl";
import { IconOutlinerEyeClosed, IconOutlinerEyeOpened, IconSortDownNew, IconSortUpNew } from "../../../assets/icons";

const RelationsControlsComponent: FC<any> = ({ relationStore }) => {
return (
<Block name="relation-controls">
<ToggleRelationsVisibilityButton relationStore={relationStore} />
<ToggleRelationsOrderButton relationStore={relationStore} />
</Block>
);
};

interface ToggleRelationsVisibilityButtonProps {
relationStore: any;
}

const ToggleRelationsVisibilityButton = observer<FC<ToggleRelationsVisibilityButtonProps>>(({ relationStore }) => {
const toggleRelationsVisibility = useCallback(
(e: any) => {
e.preventDefault();
e.stopPropagation();
relationStore.toggleAllVisibility();
},
[relationStore],
);

const isDisabled = !relationStore?.relations?.length;
const isAllHidden = !(!isDisabled && relationStore.isAllHidden);

return (
<Elem
tag={Button}
type="text"
disabled={isDisabled}
onClick={toggleRelationsVisibility}
mod={{ hidden: isAllHidden }}
aria-label={isAllHidden ? "Show all relations" : "Hide all relations"}
icon={isAllHidden ? <IconOutlinerEyeClosed /> : <IconOutlinerEyeOpened />}
tooltip={isAllHidden ? "Show all relations" : "Hide all relations"}
tooltipTheme="dark"
/>
);
});

interface ToggleRelationsOrderButtonProps {
relationStore: any;
}

const ToggleRelationsOrderButton = observer<FC<ToggleRelationsOrderButtonProps>>(({ relationStore }) => {
const toggleRelationsOrder = useCallback(
(e: any) => {
e.preventDefault();
e.stopPropagation();
relationStore.toggleOrder();
},
[relationStore],
);

const isDisabled = !relationStore?.relations?.length;
const isAsc = relationStore.order === "asc";

return (
<Elem
tag={Button}
type="text"
onClick={toggleRelationsOrder}
disabled={isDisabled}
mod={{ order: relationStore.order }}
aria-label={isAsc ? "Sort by ascending" : "Sort by descending"}
icon={isAsc ? <IconSortUpNew /> : <IconSortDownNew />}
tooltip={isAsc ? "Sort by ascending" : "Sort by descending"}
tooltipTheme="dark"
/>
);
});

export const RelationsControls = observer(RelationsControlsComponent);
34 changes: 34 additions & 0 deletions web/libs/editor/src/stores/RelationStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import Tree, { TRAVERSE_SKIP } from "../core/Tree";
import Area from "../regions/Area";
import { isDefined } from "../utils/utilities";

const localStorageKeys = {
order: "relations:order",
};

/**
* Relation between two different nodes
*/
Expand Down Expand Up @@ -102,6 +106,10 @@ const Relation = types
const RelationStore = types
.model("RelationStore", {
relations: types.array(Relation),
order: types.optional(
types.enumeration(["asc", "desc"]),
window.localStorage.getItem(localStorageKeys.order) ?? "asc",
),
})
.volatile(() => ({
showConnections: true,
Expand All @@ -115,6 +123,17 @@ const RelationStore = types
get size() {
return self.relations.length;
},
get orderedRelations() {
if (!self.relations) return [];
if (self.order === "asc") {
return self.relations.slice();
} else {
return self.relations.slice().reverse();
}
},
get isAllHidden() {
return !self.relations.find((rl) => !rl.visible);
},
get values() {
return self.control?.values ?? [];
},
Expand Down Expand Up @@ -212,6 +231,21 @@ const RelationStore = types
self.showConnections = !self.showConnections;
},

toggleOrder() {
self.order = self.order === "asc" ? "desc" : "asc";
window.localStorage.setItem(localStorageKeys.order, self.order);
},

toggleAllVisibility() {
const shouldBeHidden = !self.isAllHidden;

self.relations.forEach((rl) => {
if (rl.visible !== shouldBeHidden) {
rl.toggleVisibility();
}
});
},

setHighlight(relation) {
self._highlighted = relation.id;
},
Expand Down
Loading

0 comments on commit 9df4eb9

Please sign in to comment.