Skip to content
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
3 changes: 1 addition & 2 deletions docs/developer/contribute.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@

- **Testing**:

- **Component Testing**: Write tests for your components to ensure they work as expected. Use [Jest](https://jestjs.io/) for unit testing and snapshot testing of your React components.
- **Component Testing**: Write tests for your stories to ensure they work as expected. Use [Jest](https://jestjs.io/) for unit testing and snapshot testing of your React components.
- **Application Testing**: Use [Cypress](https://www.cypress.io/) for end-to-end testing to simulate real user interactions and ensure your application behaves correctly.
- **Test Coverage**: Maintain good test coverage to ensure that your critical features are well-protected during updates. Tools like Jest provide [coverage reports](https://jestjs.io/docs/code-coverage) that help you identify untested parts of your code.


- **Accessibility**: Make your application accessible to all users. Use semantic HTML, ARIA attributes, and test your application with different screen sizes and assistive technologies.

By following these practices, you'll ensure that your codebase remains robust, secure, and maintainable.
Expand Down
13 changes: 3 additions & 10 deletions packages/diracx-web-components/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,13 @@ const config: StorybookConfig = {
config.resolve.alias = {
...config.resolve.alias,
"@axa-fr/react-oidc": require.resolve(
"../stories/mocks/react-oidc.mock.ts",
"../stories/mocks/react-oidc.mock.tsx",
),
"@actual/react-oidc": require.resolve("@axa-fr/react-oidc"),

"@actual/hooks/metadata$": require.resolve("../src/hooks/metadata"),
"../../hooks/metadata": require.resolve(
"../stories/mocks/metadata.mock.ts",
),

"@actual/components/JobMonitor/JobDataService$": require.resolve(
"../src/components/JobMonitor/JobDataService.ts",
"../stories/mocks/metadata.mock.tsx",
),
"./JobDataService": require.resolve(
"../stories/mocks/JobDataService.mock.ts",
"../stories/mocks/JobDataService.mock.tsx",
),
};
return config;
Expand Down
6 changes: 6 additions & 0 deletions packages/diracx-web-components/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ const config = {

// The test environment that will be used for testing
testEnvironment: "jest-environment-jsdom",

moduleNameMapper: {
"^@axa-fr/react-oidc$": "<rootDir>/stories/mocks/react-oidc.mock.tsx",
"^../../hooks/metadata$": "<rootDir>/stories/mocks/metadata.mock.tsx",
"^./JobDataService$": "<rootDir>/stories/mocks/JobDataService.mock.tsx",
},
};

export default config;
2 changes: 2 additions & 0 deletions packages/diracx-web-components/jest.setup.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
import "@testing-library/jest-dom";

jest.mock("@axa-fr/react-oidc");
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { useOidcAccessToken } from "@axa-fr/react-oidc/";
import { useOidcAccessToken } from "@axa-fr/react-oidc";
import { useOIDCContext } from "../../hooks/oidcConfiguration";

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
import { ProfileButton } from "./ProfileButton";
import { ThemeToggleButton } from "./ThemeToggleButton";
import DashboardDrawer from "./DashboardDrawer";
import { ShareButton } from "./ShareButton";
import { ExportButton } from "./ExportButton";
import { ImportButton } from "./ImportButton";

interface DashboardProps {
Expand Down Expand Up @@ -124,7 +124,7 @@ export default function Dashboard({
>
<Stack direction="row" spacing={1} alignItems="center">
<ImportButton />
<ShareButton />
<ExportButton />
<ThemeToggleButton />
<ProfileButton />
</Stack>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,10 @@ export default function DashboardDrawer({
}}
>
<ListItem key={"Add application"}>
<ListItemButton onClick={() => setAppDialogOpen(true)}>
<ListItemButton
onClick={() => setAppDialogOpen(true)}
data-testid="add-application-button"
>
<ListItemIcon>{<Add />}</ListItemIcon>
<ListItemText primary={"Add application"} />
</ListItemButton>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
useTheme,
TextField,
} from "@mui/material";
import { DragIndicator } from "@mui/icons-material";
import { DragIndicator, SvgIconComponent } from "@mui/icons-material";
import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
import {
draggable,
Expand All @@ -23,14 +23,15 @@ import {
extractClosestEdge,
} from "@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge";
import { setCustomNativeDragPreview } from "@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview";
import EggIcon from "@mui/icons-material/Egg";
import { ThemeProvider } from "../../contexts/ThemeProvider";
import { useSearchParamsUtils } from "../../hooks/searchParamsUtils";
import { useApplicationId } from "../../hooks/application";
import { DashboardGroup } from "../../types";
import { DashboardGroup, DashboardItem } from "../../types";

interface DrawerItemProps {
/** The item object containing the title, id, and icon. */
item: { title: string; id: string; icon: React.ComponentType };
item: DashboardItem;
/** The index of the item. */
index: number;
/** The title of the group. */
Expand All @@ -53,7 +54,7 @@ interface DrawerItemProps {
* @returns The rendered JSX for the drawer item.
*/
export default function DrawerItem({
item: { title, id, icon },
item,
index,
groupTitle,
renamingItemId,
Expand Down Expand Up @@ -100,7 +101,7 @@ export default function DrawerItem({
width: source.element.getBoundingClientRect().width,
}}
>
<ItemPreview title={title} icon={icon} />
<ItemPreview title={item.title} icon={item.icon} />
</div>
</ThemeProvider>,
);
Expand Down Expand Up @@ -136,9 +137,9 @@ export default function DrawerItem({
const sourceIndex = source.data.index;
if (typeof sourceIndex === "number") {
const isItemBeforeSource =
index === sourceIndex - 1 && source.data.title === title;
index === sourceIndex - 1 && source.data.title === item.title;
const isItemAfterSource =
index === sourceIndex + 1 && source.data.title === title;
index === sourceIndex + 1 && source.data.title === item.title;

const isDropIndicatorHidden =
(isItemBeforeSource && closestEdge === "bottom") ||
Expand All @@ -159,7 +160,7 @@ export default function DrawerItem({
},
}),
);
}, [index, groupTitle, icon, theme, title, id]);
}, [index, groupTitle, item, theme]);

// Handle renaming of the item
const handleItemRename = () => {
Expand All @@ -169,8 +170,8 @@ export default function DrawerItem({
if (group.title === groupTitle) {
return {
...group,
items: group.items.map((item) =>
item.id === id ? { ...item, title: renameValue } : item,
items: group.items.map((i) =>
i.id === item.id ? { ...item, title: renameValue } : i,
),
};
}
Expand All @@ -185,16 +186,16 @@ export default function DrawerItem({
<>
<ListItemButton
disableGutters
key={title}
onClick={() => setParam("appId", id)}
key={item.title}
onClick={() => setParam("appId", item.id)}
sx={{ pl: 2, borderRadius: 2, pr: 1 }}
ref={dragRef}
selected={appId === id}
selected={appId === item.id}
>
<ListItemIcon>
<Icon component={icon} />
<Icon component={item.icon ?? EggIcon} />
</ListItemIcon>
{renamingItemId === id ? (
{renamingItemId === item.id ? (
<TextField
value={renameValue}
onChange={(e) => setRenameValue(e.target.value)}
Expand All @@ -211,7 +212,7 @@ export default function DrawerItem({
/>
) : (
<ListItemText
primary={title}
primary={item.title}
sx={{
whiteSpace: "nowrap",
overflow: "hidden",
Expand Down Expand Up @@ -239,15 +240,15 @@ export default function DrawerItem({
*
* @param {Object} props - The component props.
* @param {string} props.title - The title of the item.
* @param {React.ComponentType} props.icon - The icon component for the item.
* @param {SvgIconComponent} props.icon - The icon component for the item.
* @returns {JSX.Element} The rendered item preview.
*/
function ItemPreview({
title,
icon,
}: {
title: string;
icon: React.ComponentType;
icon: SvgIconComponent | null;
}) {
return (
<ListItemButton
Expand All @@ -261,7 +262,7 @@ function ItemPreview({
}}
>
<ListItemIcon>
<Icon component={icon} />
<Icon component={icon ?? EggIcon} />
</ListItemIcon>
<ListItemText
primary={title}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
AccordionSummary,
TextField,
} from "@mui/material";
import { ExpandMore, Apps } from "@mui/icons-material";
import { ExpandMore } from "@mui/icons-material";
import React, { useEffect, useRef, useState } from "react";
import { dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
import { DashboardGroup } from "../../types/DashboardGroup";
Expand Down Expand Up @@ -139,10 +139,10 @@ export default function DrawerItemGroup({
</AccordionSummary>
{/* Accordion details */}
<AccordionDetails>
{items.map(({ title: itemTitle, id, icon }, index) => (
<div onContextMenu={handleContextMenu("item", id)} key={id}>
{items.map((item, index) => (
<div onContextMenu={handleContextMenu("item", item.id)} key={item.id}>
<DrawerItem
item={{ title: itemTitle, id, icon: icon || Apps }}
item={item}
index={index}
groupTitle={title}
renamingItemId={renamingItemId}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ import { DashboardGroup } from "../../types/DashboardGroup";

import { ApplicationsContext } from "../../contexts";

interface ShareDialogProps {
interface ExportDialogProps {
open: boolean;
onClose: () => void;
state: string;
}

function ShareDialog({ open, onClose, state }: ShareDialogProps) {
function ExportDialog({ open, onClose, state }: ExportDialogProps) {
const theme = useTheme();
const handleCopy = () => {
navigator.clipboard.writeText(state);
Expand All @@ -57,11 +57,14 @@ function ShareDialog({ open, onClose, state }: ShareDialogProps) {
</Typography>
</DialogContent>
<DialogActions>
<Button onClick={onClose}>Cancel</Button>
<Button onClick={onClose} data-testid="cancel-export-button">
Cancel
</Button>
<Button
onClick={handleCopy}
startIcon={<ContentCopyIcon />}
variant="contained"
data-testid="validate-export-button"
>
Copy to Clipboard
</Button>
Expand All @@ -71,10 +74,10 @@ function ShareDialog({ open, onClose, state }: ShareDialogProps) {
}

/**
* ShareButton component allows users to share the state of selected applications.
* ExportButton component allows users to share the state of selected applications.
* It provides a menu with checkboxes for each application and a dialog to display the state.
*/
export function ShareButton() {
export function ExportButton() {
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const [dialogOpen, setDialogOpen] = useState(false);
const [selectedApps, setSelectedApps] = useState<string[]>([]);
Expand Down Expand Up @@ -103,7 +106,7 @@ export function ShareButton() {

// Function to handle the share action
// It collects the state of selected applications and opens the dialog
const handleShare = () => {
const handleExport = () => {
const states = selectedApps.map((appId) => {
const app = groups.flatMap((g) => g.items).find((a) => a.id === appId);
if (!app) return null;
Expand All @@ -123,8 +126,8 @@ export function ShareButton() {

return (
<>
<Tooltip title="Share application state">
<IconButton onClick={handleClick}>
<Tooltip title="Export application state">
<IconButton onClick={handleClick} data-testid="export-button">
<OutputIcon />
</IconButton>
</Tooltip>
Expand Down Expand Up @@ -188,16 +191,16 @@ export function ShareButton() {
<Button
fullWidth
variant="contained"
onClick={handleShare}
onClick={handleExport}
startIcon={<OutputIcon />}
>
Share {selectedApps.length} selected
Export {selectedApps.length} selected
</Button>
</Box>
)}
</Menu>

<ShareDialog
<ExportDialog
open={dialogOpen}
onClose={() => setDialogOpen(false)}
state={selectedState}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,14 @@ function ImportDialog({ open, onClose, onImport }: ImportDialogProps) {
/>
</DialogContent>
<DialogActions>
<Button onClick={onClose}>Cancel</Button>
<Button onClick={onClose} data-testid="cancel-import-button">
Cancel
</Button>
<Button
onClick={handleImport}
variant="contained"
disabled={!stateText.trim()}
data-testid="validate-import-button"
>
Import
</Button>
Expand Down Expand Up @@ -120,7 +123,10 @@ export function ImportButton() {
return (
<>
<Tooltip title="Import application state">
<IconButton onClick={() => setDialogOpen(true)}>
<IconButton
onClick={() => setDialogOpen(true)}
data-testid="import-button"
>
<InputIcon />
</IconButton>
</Tooltip>
Expand Down
Loading
Loading