Skip to content

Commit

Permalink
Feat/filters menu (#95)
Browse files Browse the repository at this point in the history
* feat(redmine #1246940): added FilterList

refactored SidebarFilters, SearchableCheckboxList, SwitchableView, added shared IFilter type
  • Loading branch information
MorganeLeCaignec authored Jan 3, 2024
1 parent 7415d9b commit 5696400
Show file tree
Hide file tree
Showing 31 changed files with 334 additions and 88 deletions.
11 changes: 11 additions & 0 deletions .changeset/new-birds-enjoy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@smile/react-front-kit-shared': minor
'@smile/react-front-kit': minor
'storybook-pages': minor
---

Added `FilterList` component, renamed `FiltersBar` in `SidebarFilters`
and `FiltersCheckboxList` in `SearchableCheckboxList`,
refactored `SearchableCheckboxList`, added common `IFilter` type in shared
package, added topContent prop to `SwitchableView`, fixed `BrowsingPage` with
name changes.
1 change: 1 addition & 0 deletions packages/react-front-kit-shared/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export type {
IOptions,
IThemeOverride,
IThemes,
IFilter,
} from './types';
// type exports
export { mainTheme, primaryTheme, secondaryTheme, themes } from './theme';
8 changes: 8 additions & 0 deletions packages/react-front-kit-shared/src/types/filters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { ReactElement } from 'react';

export interface IFilter {
active?: boolean;
component?: ReactElement;
id: number | string;
label: string;
}
1 change: 1 addition & 0 deletions packages/react-front-kit-shared/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './actions';
export * from './options';
export * from './theme';
export * from './filters';
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { IFilter } from '@smile/react-front-kit-shared';

import { Select, TextInput } from '@mantine/core';

export const filtersMock: IFilter[] = [
{
active: false,
component: <TextInput placeholder="some value" />,
id: 1,
label: 'Filter A',
},
{
active: false,
component: (
<Select
data={[
{ label: 'Some value', value: 'some value' },
{
label: 'Another value',
value: 'another value',
},
]}
value="some value"
/>
),
id: 2,
label: 'Filter B',
},
{
active: true,
component: <TextInput placeholder="some value" />,
id: 3,
label: 'Filter C',
},
{
active: true,
component: (
<Select
data={[
{ label: 'Another value', value: 'another value' },
{ label: 'Some value', value: 'some value' },
]}
value="another value"
/>
),
id: 4,
label: 'Filter D',
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { Meta, StoryObj } from '@storybook/react';

import { action } from '@storybook/addon-actions';

import { FilterList as Cmp } from './FilterList';
import { filtersMock } from './FilterList.mock';

const meta = {
component: Cmp,
tags: ['autodocs'],
title: '3-custom/Components/FilterList',
} satisfies Meta<typeof Cmp>;

export default meta;
type IStory = StoryObj<typeof meta>;

export const FilterList: IStory = {
args: {
filters: filtersMock,
onActiveFiltersChange: action('Active filters changed'),
onSubmit: action('Filters submitted'),
},
};

export const ColumnDirection: IStory = {
args: {
direction: 'column',
filters: filtersMock,
onActiveFiltersChange: action('Active filters changed'),
onSubmit: action('Filters submitted'),
spacing: 12,
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { createStyles } from '@mantine/core';

export const useStyles = createStyles(() => ({
filterComponent: {
minWidth: 184,
},
manageFilterModalBody: {
padding: '24px',
},
manageFilterModalHeader: {
marginBottom: '16px',
padding: '24px 24px 0 24px',
},
manageFilterModalTitle: {
fontSize: '18px',
fontWeight: 700,
},
manageFiltersButton: {
alignItems: 'start',
backgroundColor: 'transparent',
border: 'none',
},
manageFiltersLabel: {
gap: 4,
},
submitButton: {
minWidth: 184,
},
}));
110 changes: 110 additions & 0 deletions packages/react-front-kit/src/Components/FilterList/FilterList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
'use client';

import type { ModalProps, StackProps } from '@mantine/core';
import type { IFilter } from '@smile/react-front-kit-shared';
import type { ElementType, ReactElement } from 'react';

import { Button, Group, Modal, Stack, useMantineTheme } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { Plus } from '@phosphor-icons/react';
import { useState } from 'react';

import { SearchableCheckboxList } from '../SearchableCheckboxList/SearchableCheckboxList';

import { useStyles } from './FilterList.style';

export interface IFilterListProps
extends Omit<StackProps, 'justify' | 'onSubmit'> {
direction?: 'column' | 'row';
filters: IFilter[];
filtersManageLabel?: string;
manageFilterModalSearchPlaceholder?: string;
manageFilterModalSearchSubmit?: string;
manageFilterModalTitle?: string;
modalProps?: ModalProps;
onActiveFiltersChange?: (filters: IFilter[]) => void;
onSubmit?: (activeFilters: IFilter[]) => void;
submitLabel?: string;
}

export function FilterList(props: IFilterListProps): ReactElement {
const {
direction = 'row',
filters,
filtersManageLabel = 'Manage filters',
manageFilterModalSearchPlaceholder = 'Search in filters',
manageFilterModalSearchSubmit = 'Confirm changes',
manageFilterModalTitle = 'Manage filters',
modalProps,
onActiveFiltersChange,
onSubmit,
submitLabel = 'Filter',
...containerProps
} = props;
const [localFilters, setLocalFilters] = useState<IFilter[]>(filters);
const [manageFiltersModal, handleManageFiltersModal] = useDisclosure(false);
const Container: ElementType = direction === 'row' ? Group : Stack;

const theme = useMantineTheme();
const { classes } = useStyles();

function handleSubmit(): void {
onSubmit?.(localFilters.filter((filter) => filter.active));
}

function handleManageFiltersSubmit(activeFilters: IFilter[]): void {
handleManageFiltersModal.close();

const newFilters = localFilters.map((filter, i) =>
activeFilters[i].id === filter.id ? activeFilters[i] : filter,
);
onActiveFiltersChange?.(activeFilters);
setLocalFilters(newFilters);
}

return (
<Container spacing={10} {...containerProps}>
{localFilters
.filter((filter) => filter.active)
.map((filter) => (
<span key={filter.id} className={classes.filterComponent}>
{filter.component}
</span>
))}
{onSubmit ? (
<Button className={classes.submitButton} onClick={handleSubmit}>
{submitLabel}
</Button>
) : null}
<Button
className={classes.manageFiltersButton}
classNames={{ label: classes.manageFiltersLabel }}
onClick={handleManageFiltersModal.open}
variant="default"
>
<Plus color={theme.colors.dark[6]} size={12} />
{filtersManageLabel}
</Button>
<Modal
centered
classNames={{
body: classes.manageFilterModalBody,
header: classes.manageFilterModalHeader,
title: classes.manageFilterModalTitle,
}}
onClose={handleManageFiltersModal.close}
opened={manageFiltersModal}
size="md"
title={manageFilterModalTitle}
{...modalProps}
>
<SearchableCheckboxList<IFilter>
buttonLabel={manageFilterModalSearchSubmit}
checkboxes={filters}
onClickButton={handleManageFiltersSubmit}
placeholder={manageFilterModalSearchPlaceholder}
/>
</Modal>
</Container>
);
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const filters = [
export const checkboxesMock = [
{ active: false, id: 1, label: 'Tomato', value: 'tomato' },
{ active: false, id: 2, label: 'Banana', value: 'banana' },
{ active: true, id: 3, label: 'apple', value: 'apple' },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Meta, StoryObj } from '@storybook/react';

import { SearchableCheckboxList as Cmp } from './SearchableCheckboxList';
import { checkboxesMock } from './SearchableCheckboxList.mock';

const meta = {
component: Cmp,
tags: ['autodocs'],
title: '3-custom/Components/SearchableCheckboxList',
} satisfies Meta<typeof Cmp>;

export default meta;
type IStory = StoryObj<typeof meta>;

export const SearchableCheckboxList: IStory = {
args: {
checkboxes: checkboxesMock,
},
};
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { renderWithProviders } from '@smile/react-front-kit-shared/test-utils';

import { FiltersCheckboxList } from './FiltersCheckboxList';
import { filters } from './FiltersCheckboxList.mock';
import { SearchableCheckboxList } from './SearchableCheckboxList';
import { checkboxesMock } from './SearchableCheckboxList.mock';

describe('FiltersCheckboxList', () => {
describe('SearchableCheckboxList', () => {
beforeEach(() => {
// Prevent mantine random ID
Math.random = () => 0.42;
});
it('matches snapshot', () => {
const { container } = renderWithProviders(
<FiltersCheckboxList
<SearchableCheckboxList
buttonLabel="Test"
filters={filters}
checkboxes={checkboxesMock}
placeholder="Test"
/>,
);
Expand Down
Loading

0 comments on commit 5696400

Please sign in to comment.