Skip to content

Commit

Permalink
feat(filter): L3-4114 - create dumb filter component (#418)
Browse files Browse the repository at this point in the history
Co-authored-by: Zachary Rose <[email protected]>
Co-authored-by: Scott Dickerson <[email protected]>
  • Loading branch information
3 people authored Nov 22, 2024
1 parent c816a5d commit 0014771
Show file tree
Hide file tree
Showing 32 changed files with 1,278 additions and 113 deletions.
81 changes: 41 additions & 40 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
"stylelint-order": "^6.0.4",
"stylelint-scss": "^6.4.1",
"ts-node": "^10.9.2",
"type-fest": "^4.27.0",
"typescript": "^5.5.4",
"vite": "^5.4.6",
"vite-plugin-dts": "^4.2.4",
Expand Down
16 changes: 9 additions & 7 deletions src/componentStyles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@
@use 'components/Tags/tags';
@use 'components/Countdown/countdown';
@use 'components/Countdown/duration';
@use 'components/Detail/detail';
@use 'components/PinchZoom/pinchZoom';
@use 'components/Tabs/tabs';
@use 'components/SeldonImage/seldonImage';
@use 'components/Filter/filter';

// Patterns
@use 'patterns/HeroBanner/heroBanner';
Expand All @@ -49,14 +54,11 @@
@use 'patterns/Subscribe/subscribe';
@use 'patterns/Social/social';
@use 'patterns/SaleHeaderBanner/saleHeaderBanner';
@use 'patterns/ObjectTile/objectTile';
@use 'patterns/BidSnapshot/bidSnapshot';
@use 'patterns/FilterMenu/filterMenu';
@use 'patterns/DetailList/detailList';

// Site Furniture
@use 'site-furniture/Header/header';
@use 'site-furniture/Footer/footer';
@use 'components/Detail/detail';
@use 'patterns/DetailList/detailList';
@use 'components/PinchZoom/pinchZoom';
@use 'components/Tabs/tabs';
@use 'components/SeldonImage/seldonImage';
@use 'patterns/ObjectTile/objectTile';
@use 'patterns/BidSnapshot/bidSnapshot';
6 changes: 3 additions & 3 deletions src/components/DatePicker/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import classnames from 'classnames';
import { noOp, useNormalizedInputProps } from '../../utils';
import Input, { InputProps } from '../Input/Input';

export interface DatePickerProps extends Omit<InputProps, 'defaultValue'>, Record<string, unknown> {
export interface DatePickerProps extends Omit<InputProps, 'defaultValue' | 'onChange'>, Record<string, unknown> {
/**
* Optionally allow manual entry to the date input
*/
Expand Down Expand Up @@ -181,12 +181,12 @@ const DatePicker = React.forwardRef(
};
}, [allowInput, defaultValue, inputProps.disabled, enableTime, id, locale, onChange, ref, type]);

const handleKeyPress = (e: KeyboardEvent) => {
const handleKeyPress: React.KeyboardEventHandler<HTMLInputElement> = (e) => {
if (allowInput && /[0-9-/:]+/g.test(e.key)) {
manualValue.current = fp?.current?.selectedDates;
}
};
const handOnBlur = (e: FocusEvent) => {
const handOnBlur: React.FocusEventHandler<HTMLInputElement> = (e) => {
if (
!allowInput ||
(e.relatedTarget instanceof HTMLElement && e.relatedTarget?.classList.value.includes('flatpickr'))
Expand Down
2 changes: 1 addition & 1 deletion src/components/ErrorBoundary/ErrorBoundary.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const VolatileComponent = ({ throwError }: WrappedProps) => {
throwError();
}
}, [throwError]);
return <Input {...reqProps} defaultValue={['2023-06-01T08:30', '2023-06-05T08:30']} />;
return <Input {...reqProps} defaultValue="2023-06-01T08:30" />;
};

describe('An ErrorBoundary', () => {
Expand Down
99 changes: 99 additions & 0 deletions src/components/Filter/Filter.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { Meta } from '@storybook/react';
import Filter from './Filter';
import FilterHeader from './FilterHeader';
import FilterInput from './FilterInput';
import { useState } from 'react';

// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
const meta = {
title: 'Components/Filter',
component: Filter,
} satisfies Meta<typeof Filter>;

export default meta;

type FilterDimension = { label: string; disabled?: boolean | undefined; checked: boolean };

type FilterType = {
label: string;
id: string;
filterDimensions: FilterDimension[];
};

type PropTypes = {
filter: FilterType;
viewAllLimit: number;
isViewingAll: boolean;
};

const filter: FilterType = {
label: 'Artists & Makers',
id: 'makers',
filterDimensions: [
{ label: 'Jimmy', checked: true },
{ label: 'Bob', checked: false },
{ label: 'Alan', checked: false },
{ label: 'Nick', checked: false },
{ label: 'Joe', checked: false },
{ label: 'Fred', checked: false },
{ label: 'Rob', checked: false },
{ label: 'Roy', checked: false },
{ label: 'disabled', disabled: true, checked: false },
],
};

const FilterComponent = (props: PropTypes) => {
const { filter: intialFilters, viewAllLimit, isViewingAll } = props;
const [filter, setFilter] = useState(intialFilters);

const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
const { checked, name } = e.target as HTMLInputElement;
const updatedFilterDimensions = filter.filterDimensions.map((dimension) => {
if (dimension.label === name) {
return {
...dimension,
checked,
};
}
return dimension;
});

const updatedFilter = {
...filter,
filterDimensions: updatedFilterDimensions,
};

setFilter(updatedFilter);
};

return (
<Filter key={filter.label} name={filter.label} viewAllLimit={viewAllLimit} isViewingAll={isViewingAll}>
<FilterHeader heading={filter.label} />
{filter.filterDimensions.map((value: FilterDimension) => (
<FilterInput
id={value.label}
key={value.label}
labelText={value.label}
onChange={handleChange}
type="checkbox"
disabled={value?.disabled}
name={value.label}
checked={value.checked}
/>
))}
</Filter>
);
};

export const Playground = (props: PropTypes) => {
return <FilterComponent {...props} />;
};

// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
Playground.args = {
filter,
viewAllLimit: 10,
isViewingAll: false,
};

Playground.argTypes = {};
74 changes: 74 additions & 0 deletions src/components/Filter/Filter.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import Filter from './Filter';
import { runCommonTests } from '../../utils/testUtils';
import { render, screen } from '@testing-library/react';
import FilterHeader from './FilterHeader';
import FilterInput from './FilterInput';

describe('Filter', () => {
runCommonTests(Filter, 'Filter');

it('renders the different input types of filter values', () => {
const handleChange = vi.fn();

render(
<Filter name="test">
<FilterInput
type="checkbox"
onChange={handleChange}
id="test[test1]"
labelText="Filter 1"
name="test[test1]"
checked={true}
/>
<FilterInput
type="radio"
onChange={handleChange}
id="test[test2]"
labelText="Filter 2"
name="test[test2]"
checked={true}
/>
</Filter>,
);

expect(screen.getAllByText('Filter 1')[0]).toBeInTheDocument();
expect(screen.getAllByText('Filter 2')[0]).toBeInTheDocument();

const checkbox = screen.getByRole('checkbox');
const radio = screen.getByRole('radio');

expect(checkbox).toHaveAttribute('type', 'checkbox');
expect(radio).toHaveAttribute('type', 'radio');
});

it('renders a filter header', () => {
render(
<Filter name="test">
<FilterHeader heading="Filter Header 1" />
</Filter>,
);

expect(screen.getByText('Filter Header 1')).toBeInTheDocument();
});

it('should disable filters when disabled prop is passed', () => {
const handleChange = vi.fn();

render(
<Filter name="test">
<FilterInput
type="checkbox"
onChange={handleChange}
id="test[test1]"
labelText="Filter 1"
name="test[test1]"
checked={true}
disabled={true}
/>
</Filter>,
);

const checkbox = screen.getByRole('checkbox');
expect(checkbox).toBeDisabled();
});
});
Loading

0 comments on commit 0014771

Please sign in to comment.