Skip to content

Commit

Permalink
Merge pull request #401 from LifeSG/filter-item-tooltip
Browse files Browse the repository at this point in the history
Add filter item tooltip
  • Loading branch information
keithtxw authored Feb 6, 2024
2 parents fb4496b + 8fa1d06 commit 1478515
Show file tree
Hide file tree
Showing 27 changed files with 1,124 additions and 520 deletions.
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module.exports = {
"<rootDir>/src/__mocks__/fileMock.js",
"\\.(css|less)$": "identity-obj-proxy",
},
moduleDirectories: ["node_modules", "."],
moduleDirectories: ["node_modules", "<rootDir>"],
setupFilesAfterEnv: ["jest-canvas-mock", "@testing-library/jest-dom"],
reporters: ["default", ["jest-junit", { outputName: "junit.xml" }]],
};
763 changes: 419 additions & 344 deletions package-lock.json

Large diffs are not rendered by default.

16 changes: 9 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"scripts": {
"build": "npm run rollup && npm run post:build",
"build-check": "npm run rollup rollup.check.config.js",
"rollup": "rm -rf dist && rollup -c",
"rollup": "rm -rf dist && rollup --bundleConfigAsCjs -c",
"pack-package": "cd dist && npm pack",
"post:build": "node ./scripts/post-build.js",
"publish-lib": "npm publish ./dist",
Expand All @@ -33,6 +33,8 @@
},
"homepage": "https://github.com/LifeSG/react-design-system#readme",
"dependencies": {
"@floating-ui/dom": "^1.5.4",
"@floating-ui/react": "^0.26.6",
"immer": "^10.0.2",
"react-slider": "^2.0.6",
"react-zoom-pan-pinch": "^3.3.0"
Expand All @@ -46,10 +48,10 @@
"@dnd-kit/sortable": "^7.0.2",
"@lifesg/react-icons": "^1.4.0",
"@react-theming/storybook-addon": "^1.1.10",
"@rollup/plugin-commonjs": "^21.0.2",
"@rollup/plugin-image": "^2.1.1",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.1.3",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-image": "^3.0.3",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.2.3",
"@storybook/addon-a11y": "^7.4.6",
"@storybook/addon-actions": "^7.4.6",
"@storybook/addon-essentials": "^7.4.6",
Expand Down Expand Up @@ -104,12 +106,12 @@
"react-responsive": "^9.0.0-beta.10",
"react-spring": "~9.7.3",
"remark-gfm": "^3.0.1",
"rollup": "^2.70.1",
"rollup": "^3.29.4",
"rollup-plugin-generate-package-json": "^3.2.0",
"rollup-plugin-peer-deps-external": "^2.2.4",
"rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.31.2",
"rollup-plugin-typescript2": "^0.36.0",
"storybook": "^7.4.6",
"style-loader": "^2.0.0",
"styled-components": "^5.3.5",
Expand Down
2 changes: 2 additions & 0 deletions rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,14 @@ export default [
format: "esm",
sourcemap: true,
exports: "named",
interop: "compat",
},
{
file: pkg.main,
format: "cjs",
sourcemap: true,
exports: "named",
interop: "compat",
},
],
plugins,
Expand Down
4 changes: 3 additions & 1 deletion src/filter/filter-context.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React from "react";
import React, { MutableRefObject } from "react";
import { Mode } from "./types";

interface IFilterContext {
mode: Mode;
rootNode: MutableRefObject<HTMLDivElement> | null;
}

export const FilterContext = React.createContext<IFilterContext>({
mode: "default",
rootNode: null,
});
21 changes: 19 additions & 2 deletions src/filter/filter-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import isNil from "lodash/isNil";
import { useContext, useEffect, useState } from "react";
import { useResizeDetector } from "react-resize-detector";
import { useSpring } from "react-spring";
import { PopoverAddon } from "../form/form-label-addon";
import { FilterContext } from "./filter-context";
import {
ChevronIcon,
Expand All @@ -27,13 +28,14 @@ export const FilterItem = ({
showDivider = true,
showMobileDivider = true,
title,
addon,
children,
...otherProps
}: FilterItemProps) => {
// =============================================================================
// CONST, STATE, REF
// =============================================================================
const { mode } = useContext(FilterContext);
const { mode, rootNode } = useContext(FilterContext);
const isMobile = mode === "mobile";
const [expanded, setExpanded] = useState(getInitialExpandState());
const [contentMinimised, setContentMinimised] = useState(minimisable);
Expand Down Expand Up @@ -90,6 +92,21 @@ export const FilterItem = ({
// =============================================================================
// RENDER
// =============================================================================

const renderAddon = () => {
switch (addon.type) {
case "popover":
return (
<PopoverAddon
addon={addon}
rootNode={isMobile ? rootNode : undefined}
/>
);
default:
return null;
}
};

return (
<FilterItemWrapper $collapsible={collapsible}>
<Divider
Expand All @@ -100,7 +117,7 @@ export const FilterItem = ({
<FilterItemHeader>
{title && (
<FilterItemTitle weight="semibold">
{title}
{title} {addon && renderAddon()}
</FilterItemTitle>
)}
{collapsible && (
Expand Down
18 changes: 5 additions & 13 deletions src/filter/filter.styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,6 @@ import { Text } from "../text/text";
// CONTAINER STYLES
// =============================================================================

export const DesktopView = styled.div`
${MediaQuery.MaxWidth.tablet} {
display: none;
}
`;

export const MobileView = styled.div`
display: none;
${MediaQuery.MaxWidth.tablet} {
display: block;
}
`;

export const DesktopContainer = styled.div`
background-color: ${Color.Neutral[8]};
border: 1px solid ${Color.Neutral[5]};
Expand All @@ -39,6 +26,11 @@ export const MobileContainer = styled.div`
flex-direction: column;
`;

export const MobileOverlayContainer = styled.div`
height: 100%;
width: 100%;
`;

export const FilterBody = styled.div`
flex: 1;
width: 100%;
Expand Down
70 changes: 38 additions & 32 deletions src/filter/filter.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CrossIcon } from "@lifesg/react-icons/cross";
import { useState } from "react";
import { useEffect, useRef, useState } from "react";
import { useMediaQuery } from "react-responsive";
import { Overlay } from "../overlay/overlay";
import { MediaWidths } from "../spec/media-spec";
Expand All @@ -9,7 +9,6 @@ import { FilterItem } from "./filter-item";
import { FilterItemPage } from "./filter-item-page";
import {
DesktopContainer,
DesktopView,
FilterBody,
FilterButton,
FilterClearButton,
Expand All @@ -19,7 +18,7 @@ import {
FilterHeaderButton,
FilterTitle,
MobileContainer,
MobileView,
MobileOverlayContainer,
StyledFilterIcon,
} from "./filter.styles";
import { FilterProps, Mode } from "./types";
Expand All @@ -38,21 +37,20 @@ const FilterBase = ({
// CONST, STATE, REF
// =============================================================================
const [visible, setVisible] = useState(false);
const mobileNodeRef = useRef<HTMLDivElement>(null);
const desktopNodeRef = useRef<HTMLDivElement>(null);
const isMobile = useMediaQuery({
maxWidth: MediaWidths.tablet,
});

// =============================================================================
// EFFECTS
// =============================================================================
useMediaQuery(
{
maxWidth: MediaWidths.tablet,
},
undefined,
(isMobile) => {
if (!isMobile) {
handleDismissFilter();
}
useEffect(() => {
if (!isMobile) {
handleDismissFilter();
}
);
}, [isMobile]);

// =========================================================================
// EVENT HANDLERS
Expand Down Expand Up @@ -126,26 +124,31 @@ const FilterBase = ({
<StyledFilterIcon /> {toggleFilterButtonLabel}
</FilterButton>
<Overlay show={visible} disableTransition>
<MobileContainer data-testid="filter-mobile">
{renderHeader("mobile")}
<FilterBody>{renderChildren("mobile")}</FilterBody>
<FilterFooter>
<FilterDoneButton
onClick={handleDoneClick}
type="button"
>
Done
</FilterDoneButton>
</FilterFooter>
</MobileContainer>
<MobileOverlayContainer>
<MobileContainer
data-testid="filter-mobile"
ref={mobileNodeRef}
>
{renderHeader("mobile")}
<FilterBody>{renderChildren("mobile")}</FilterBody>
<FilterFooter>
<FilterDoneButton
onClick={handleDoneClick}
type="button"
>
Done
</FilterDoneButton>
</FilterFooter>
</MobileContainer>
</MobileOverlayContainer>
</Overlay>
</>
);
};

const renderDesktop = () => {
return (
<DesktopContainer data-testid="filter-desktop">
<DesktopContainer data-testid="filter-desktop" ref={desktopNodeRef}>
{renderHeader("default")}
{renderChildren("default")}
</DesktopContainer>
Expand All @@ -154,16 +157,19 @@ const FilterBase = ({

return (
<div {...otherProps}>
<MobileView>
<FilterContext.Provider value={{ mode: "mobile" }}>
{isMobile ? (
<FilterContext.Provider
value={{ mode: "mobile", rootNode: mobileNodeRef }}
>
{renderMobile()}
</FilterContext.Provider>
</MobileView>
<DesktopView>
<FilterContext.Provider value={{ mode: "default" }}>
) : (
<FilterContext.Provider
value={{ mode: "default", rootNode: desktopNodeRef }}
>
{renderDesktop()}
</FilterContext.Provider>
</DesktopView>
)}
</div>
);
};
Expand Down
5 changes: 4 additions & 1 deletion src/filter/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { FormLabelAddonProps } from "../form/types";

export type Mode = "default" | "mobile";

export interface FilterProps {
Expand Down Expand Up @@ -25,6 +27,7 @@ export interface FilterItemProps {
/** Specifies if divider is visible in mobile mode */
showMobileDivider?: boolean | undefined;
title?: string | undefined;
addon?: FormLabelAddonProps | undefined;
className?: string | undefined;
id?: string | undefined;
"data-testid"?: string | undefined;
Expand Down Expand Up @@ -54,7 +57,7 @@ export interface FilterItemCheckboxProps<T>
selectedOptions?: T[] | undefined;
onSelect?: ((options: T[]) => void) | undefined;
/** Function to derive display value from an item. If not set, checks `item.label`. */
labelExtractor?: ((item: T) => string) | undefined;
labelExtractor?: ((item: T) => React.ReactNode) | undefined;
/** Function to derive value from an item. If not set, checks `item.value`. */
valueExtractor?: ((item: T) => string) | undefined;
}
Loading

0 comments on commit 1478515

Please sign in to comment.