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

Explorer columns order #625

Closed
wants to merge 8 commits into from
Closed
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
16 changes: 9 additions & 7 deletions apps/data-studio/src/components/Router/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,15 @@ const Router: FunctionComponent = () => {
</Header>
<Layout style={{overflow: 'hidden', position: 'relative'}}>
<Explorer.EditSettingsContextProvider>
<Content style={{background: themeVars.defaultBg, overflow: 'hidden'}}>
<UserPanel userPanelVisible={userPanelVisible} hideUserPanel={hideUserPanel} />
<NotifsPanel setNbNotifs={_setNbNotifs} />
<Routes />
</Content>
{/* TODO: handle conflict with app side panel */}
<Explorer.SettingsSidePanel />
<Explorer.ViewSettingsContextProvider>
<Content style={{background: themeVars.defaultBg, overflow: 'hidden'}}>
<UserPanel userPanelVisible={userPanelVisible} hideUserPanel={hideUserPanel} />
<NotifsPanel setNbNotifs={_setNbNotifs} />
<Routes />
</Content>
{/* TODO: handle conflict with app side panel */}
<Explorer.SettingsSidePanel />
</Explorer.ViewSettingsContextProvider>
</Explorer.EditSettingsContextProvider>
</Layout>
</Layout>
Expand Down
52 changes: 52 additions & 0 deletions docs/adr/ADR-001-dnd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
Copyright LEAV Solutions 2017 until 2023/11/05, Copyright Aristid from 2023/11/06
This file is released under LGPL V3
License text available at https://www.gnu.org/licenses/lgpl-3.0.txt
*/
# Drag and drop library

Date: 19/11/2024

## Context:

We used to use [react-beautiful-dnd](https://github.com/atlassian/react-beautiful-dnd) to handle drag and drop on LEAV. Unfortunately, this library is not maintained anymore by Atlassian and we need to find a new one for future development and eventually migrate previous code.

### Options:
1. [pragmatic-drag-and-drop](https://github.com/atlassian/pragmatic-drag-and-drop)
- Pros:
- Maintained by Atlassian (the team behind react-beautiful-dnd)
- Actively maintained
- Cons:
- Unknown by the team

2. [dnd-kit](https://github.com/clauderic/dnd-kit)
- Pros:
- Known by the team (already used in other projects)
- Lot of embedded use cases (lists, grid...)
- Quite small (about 10kb)
- Popular (~13k stars on Github)
- Cons:
- Migration needed for existing code

3. [react-dnd](https://github.com/react-dnd/react-dnd)
- Pros:
- Very modular
- Very popular (~21k stars on Github)
- Cons:
- Steep learning curve
- Low maintenance (last release in 2022)

4. Any fork of react-beautiful-dnd
- Pros:
- Same API as react-beautiful-dnd.
- No migration needed for existing code.
- Cons:
- Maintenance is pretty random as it might depend on small teams or even individuals

## Sources
- https://github.com/atlassian/react-beautiful-dnd/issues/2672

## Decision:

We'll be using dnd-kit for future development as it's a very complete library, easy to use, with a good accessibility and already known by the team.
Existing code might be eventually migrated but there's no rush as it works fine for now.
28 changes: 28 additions & 0 deletions docs/adr/ADR.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
Copyright LEAV Solutions 2017 until 2023/11/05, Copyright Aristid from 2023/11/06
This file is released under LGPL V3
License text available at https://www.gnu.org/licenses/lgpl-3.0.txt
*/
# Architecture Decision Record

**/!\ All bellowing decisions are specification to this repo.**

## Format

```markdown
### XXX

Date: dd/mm/yyyy

#### Context:

Context of decision

#### Decision:

Decision taken
```

## Decisions log

1. [Drag and drop library](ADR-001-dnd.md)
2 changes: 2 additions & 0 deletions libs/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@
"dependencies": {
"@ckeditor/ckeditor5-build-inline": "39.0.1",
"@ckeditor/ckeditor5-react": "6.1.0",
"@dnd-kit/core": "6.1.0",
"@dnd-kit/sortable": "8.0.0",
"@leav/utils": "0.0.3",
"dompurify": "3.1.3",
"html-react-parser": "4.2.2",
Expand Down
9 changes: 8 additions & 1 deletion libs/ui/src/components/Explorer/DataView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
PropertyValueValueFragment
} from '_ui/_gqlTypes';
import {IItemAction, IItemData, ItemWhoAmI} from './_types';
import {useGetLibraryColumns} from './edit-settings/useGetLibraryColumns';

const USELESS = '';

Expand All @@ -32,15 +33,18 @@ const _getIdCard = ({id, label, library, preview, subLabel}: ItemWhoAmI): Return
interface IDataViewProps {
dataGroupedFilteredSorted: IItemData[];
itemActions: IItemAction[];
library: string;
attributesToDisplay: string[];
}

export const DataView: FunctionComponent<IDataViewProps> = ({
dataGroupedFilteredSorted,
attributesToDisplay,
library,
itemActions
}) => {
const {t} = useSharedTranslation();
const {attributeDetailsById} = useGetLibraryColumns(library);

const _getActionButtons = (actions: Array<Override<IItemAction, {callback: () => void}>>): ReactNode => {
const isLessThanFourActions = actions.length < 4;
Expand Down Expand Up @@ -87,6 +91,9 @@ export const DataView: FunctionComponent<IDataViewProps> = ({
);
};

const _getColumnName = (attributeName: string): string =>
attributeDetailsById[attributeName].label ?? attributeName;

const renderCell = (propertiesById: IItemData['propertiesById'], attributeName: string) => {
// TODO: handle inherited and calculated values
const isLinkValue = (v: PropertyValueFragment): v is PropertyValueLinkValueFragment =>
Expand Down Expand Up @@ -116,7 +123,7 @@ export const DataView: FunctionComponent<IDataViewProps> = ({

const columns = attributesToDisplay
.map<KitTableColumnType<IItemData>>(attributeName => ({
title: attributeName,
title: attributeName === 'whoAmI' ? '' : _getColumnName(attributeName),
dataIndex: USELESS,
render: (_, {whoAmI, propertiesById}) =>
attributeName === 'whoAmI' ? _getIdCard(whoAmI) : renderCell(propertiesById, attributeName)
Expand Down
9 changes: 6 additions & 3 deletions libs/ui/src/components/Explorer/Explorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {useEditAction} from './useEditAction';
import {usePrimaryActionsButton as usePrimaryActionsButton} from './usePrimaryActions';
import {ExplorerTitle} from './ExplorerTitle';
import {useCreateAction} from './useCreateAction';
import {useViewSettings} from './edit-settings/useViewSettings';
import {useGetLibraryColumns} from './edit-settings/useGetLibraryColumns';

interface IExplorerProps {
library: string;
Expand Down Expand Up @@ -40,9 +42,9 @@ export const Explorer: FunctionComponent<IExplorerProps> = ({
defaultActionsForItem = ['edit', 'deactivate'],
defaultPrimaryActions = ['create']
}) => {
const currentAttribute = 'id';
const {view} = useViewSettings();

const {data, loading, refetch} = useExplorerData(library, [currentAttribute]); // TODO: refresh when go back on page
const {data, loading, refetch} = useExplorerData(library, view.fields); // TODO: refresh when go back on page

const {deactivateAction} = useDeactivateAction({
isEnabled: isNotEmpty(defaultActionsForItem) && defaultActionsForItem.includes('deactivate')
Expand Down Expand Up @@ -82,9 +84,10 @@ export const Explorer: FunctionComponent<IExplorerProps> = ({
</KitSpace>
</ExplorerHeaderDivStyled>
<DataView
library={library}
dataGroupedFilteredSorted={data ?? []}
attributesToDisplay={[currentAttribute, 'whoAmI']}
itemActions={dedupItemActions}
attributesToDisplay={['whoAmI', ...view.fields]}
/>
</>
)}
Expand Down
60 changes: 48 additions & 12 deletions libs/ui/src/components/Explorer/edit-settings/ColumnItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
// License text available at https://www.gnu.org/licenses/lgpl-3.0.txt
import {KitTypography} from 'aristid-ds';
import {FunctionComponent, ReactNode} from 'react';
import {FaChevronRight, FaEye, FaEyeSlash, FaGripLines} from 'react-icons/fa';
import {FaEye, FaEyeSlash} from 'react-icons/fa';
import styled from 'styled-components';
import {useSortable} from '@dnd-kit/sortable';
import {CSS} from '@dnd-kit/utilities';
import {useSharedTranslation} from '_ui/hooks/useSharedTranslation';

const StyledValue = styled(KitTypography.Text)`
color: var(--general-utilities-disabled);
Expand Down Expand Up @@ -60,11 +63,16 @@ const StyledEyeSlash = styled(FaEyeSlash)`
color: var(--general-utilities-disabled);
`;

const StyledDragHandle = styled.span<{$isDragging: boolean}>`
cursor: ${props => (props.$isDragging ? 'grabbing' : 'grab')};
`;

const StyledEmptyIcon = styled.div`
width: calc(var(--general-spacing-s) * 1px);
`;

interface IColumnItemProps {
itemId: string;
dragHandler?: ReactNode;
visible: boolean;
title: string;
Expand All @@ -74,19 +82,47 @@ interface IColumnItemProps {
}

export const ColumnItem: FunctionComponent<IColumnItemProps> = ({
itemId,
dragHandler,
title,
disabled,
visible,
onVisibilityClick
}) => (
<StyledConfigurationItem className={disabled ? 'disabled' : ''}>
{dragHandler || <StyledEmptyIcon />}
<KitTypography.Text size="fontSize5" ellipsis className="title">
{title}
</KitTypography.Text>
<button disabled={disabled} onClick={onVisibilityClick}>
{visible ? <StyledFaEye /> : <StyledEyeSlash />}
</button>
</StyledConfigurationItem>
);
}) => {
const {t} = useSharedTranslation();
const {attributes, listeners, setNodeRef, transform, transition, isDragging} = useSortable({id: itemId});
const style = {
transform: CSS.Transform.toString(transform),
transition
};

const _handleClick = () => {
if (onVisibilityClick) {
onVisibilityClick();
}
};

const visibilityButtonLabel = visible ? t('explorer.hide') : t('explorer.show');
return (
<StyledConfigurationItem className={disabled ? 'disabled' : ''} ref={setNodeRef} style={style}>
{dragHandler ? (
<StyledDragHandle {...attributes} {...listeners} $isDragging={isDragging}>
{dragHandler}
</StyledDragHandle>
) : (
<StyledEmptyIcon />
)}
<KitTypography.Text size="fontSize5" ellipsis className="title">
{title}
</KitTypography.Text>
<button
disabled={disabled}
onClick={_handleClick}
title={visibilityButtonLabel}
aria-label={visibilityButtonLabel}
>
{visible ? <StyledFaEye /> : <StyledEyeSlash />}
</button>
</StyledConfigurationItem>
);
};
12 changes: 7 additions & 5 deletions libs/ui/src/components/Explorer/edit-settings/DisplayMode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import {useSharedTranslation} from '_ui/hooks/useSharedTranslation';
import {KitRadio, KitSpace, KitTag} from 'aristid-ds';
import {RadioChangeEvent} from 'aristid-ds/dist/Kit/DataEntry/Radio';
import {FunctionComponent, useState} from 'react';
import {FunctionComponent} from 'react';
import styled from 'styled-components';
import {DisplayModeTable} from './DisplayModeTable';
import {useViewSettings} from './useViewSettings';
import {ViewSettingsActionTypes} from './viewSettingsReducer';

const StyledWrapperDiv = styled.div`
display: flex;
Expand All @@ -24,17 +26,17 @@ interface IDisplayModeProps {

export const DisplayMode: FunctionComponent<IDisplayModeProps> = ({library}) => {
const {t} = useSharedTranslation();
const [currentDisplayMode, setCurrentDisplayMode] = useState('table');
const {view, dispatch} = useViewSettings();

const _handleDisplayModeChange = (event: RadioChangeEvent) => {
setCurrentDisplayMode(event.target.value);
dispatch({type: ViewSettingsActionTypes.CHANGE_DISPLAY_MODE, displayMode: event.target.value});
};

const comingSoonTag = <KitTag type="secondary" idCardProps={{description: String(t('explorer.coming-soon'))}} />;

return (
<StyledWrapperDiv>
<KitRadio.Group value={currentDisplayMode} onChange={_handleDisplayModeChange}>
<KitRadio.Group value={view.displayMode} onChange={_handleDisplayModeChange}>
<KitSpace direction="vertical" size={0}>
<KitRadio value="list" disabled>
<KitSpace>
Expand All @@ -54,7 +56,7 @@ export const DisplayMode: FunctionComponent<IDisplayModeProps> = ({library}) =>
</KitRadio>
</KitSpace>
</KitRadio.Group>
{currentDisplayMode === 'table' && <DisplayModeTable library={library} />}
{view.displayMode === 'table' && <DisplayModeTable library={library} />}
</StyledWrapperDiv>
);
};
Loading
Loading