Sort & Filter
Allow users to rearrange and temporarily remove items from a list of content.
A lengthy list of content may need a way to be controlled by the viewer to reveal wanted content by sorting it in a different order, or by filtering out unneeded items.
Sorting is a method in which the set of content is arranged in a particular order. Commonly this could be alphabetically, or by date. A user might change the sort order to find particular content quickly or to line up content in a specific order to compare them.
const SortExample = () => {const [value, setValue] = React.useState(0);const [open, setOpen] = React.useState(false);const onChangeHandler = (e) => {setValue(e);setOpen(false);};const sortOptions = [{text: 'Name: A to Z',value: 0,},{text: 'Name: Z to A',value: 1,},];const arr = ['Alaska','Colorado','California','Michigan','New York',];const myData = value === 0 ? arr.sort((a, b) => a.localeCompare(b)) : arr.sort((a, b) => b.localeCompare(a));return (<Stack direction="column" spacing={2}><Popoverel="span"direction="br"padding="s"onClickOutside={() => setOpen(false)}open={open}portal // This is optionaltrigger={<FilterButtononClick={() => setOpen(!open)}value={value !== 0 && sortOptions[value].text}label="Sort"expandIcon={open}/>}><OptionList options={sortOptions} onChange={onChangeHandler} /></Popover><Stack direction="column" spacing={1}>{myData.map((e, i) => <Card padding="thin" key={i}>{e}</Card>)}</Stack></Stack>);};render (SortExample)
A filter hides all content except the what matches the selected criteria. Common filters might be status, time frame, person assigned, category, etc. If a filter is off, that means all content is being shown.
const FilterExample = () => {const [value, setValue] = React.useState([]);const [open, setOpen] = React.useState(false);const onChangeHandler = (data, checked) => {if (checked) {setValue((prevState) => [...prevState, data]);} else {setValue((prevState) =>[...prevState].filter((item) => item !== data));}};const multiOptions = [{text: 'Country',value: 0,},{text: 'City',value: 1,},];const arr = [{name: 'Los Angeles', type: 'city'},{name: 'Mexico', type: 'country'},{name: 'Seoul', type: 'city'},{name: 'Spain', type: 'country'},{name: 'USA', type: 'country'},];return (<Stack direction="column" spacing={2}><Popoverel="span"direction="br"padding="s"onClickOutside={() => setOpen(false)}open={open}portal // This is optionaltrigger={<FilterButtononClick={() => setOpen(!open)}value={value.length > 0 && value.length}label="Type"expandIcon={open}/>}><OptionListmultipleoptions={multiOptions}onChange={onChangeHandler}value={value}/></Popover><Stack direction="column" spacing={1}>{arr.map((e, i) => {const valueArr = value.map((e) => multiOptions[e].text.toLowerCase());if (valueArr.length === 0) return <Card padding="thin" key={i}>{e.name}</Card>if (valueArr.includes(e.type)) return <Card padding="thin" key={i}>{e.name}</Card>})}</Stack></Stack>);};render (FilterExample)
Sort and filter are powerful when they are combined as they allow the user to redisplay content exactly how they would like to view it.
const SortFilterExample = () => {const [filterValue, setFilterValue] = React.useState([]);const [sortValue, setSortValue] = React.useState(0);const [sortOpen, setSortOpen] = React.useState(false);const [filterOpen, setFilterOpen] = React.useState(false);const filterChangeHandler = (data, checked) => {if (checked) {setFilterValue((prevState) => [...prevState, data]);} else {setFilterValue((prevState) =>[...prevState].filter((item) => item !== data));}};const sortChangeHandler = (e) => {setSortValue(e);setSortOpen(false);};const sortOptions = [{text: 'Name: A to Z',value: 0,},{text: 'Name: Z to A',value: 1,},];const multiOptions = [{text: 'Country',value: 0,},{text: 'City',value: 1,},];const arr = [{name: 'Los Angeles', type: 'city'},{name: 'Mexico', type: 'country'},{name: 'Seoul', type: 'city'},{name: 'Spain', type: 'country'},{name: 'USA', type: 'country'},];const myData = sortValue === 0 ? arr.sort((a, b) => a.name.localeCompare(b.name)) : arr.sort((a, b) => b.name.localeCompare(a.name));return (<Stack direction="column" spacing={2}><Stack spacing={1}><Popoverel="span"direction="br"padding="s"onClickOutside={() => setFilterOpen(false)}open={filterOpen}portal // This is optionaltrigger={<FilterButtononClick={() => setFilterOpen(!filterOpen)}value={filterValue.length > 0 && filterValue.length}label="Type"expandIcon={filterOpen}/>}><OptionListmultipleoptions={multiOptions}onChange={filterChangeHandler}value={filterValue}/></Popover><Popoverel="span"direction="br"padding="s"onClickOutside={() => setSortOpen(false)}open={sortOpen}portal // This is optionaltrigger={<FilterButtononClick={() => setSortOpen(!sortOpen)}value={sortValue !== 0 && sortOptions[sortValue].text}label="Sort"expandIcon={sortOpen}/>}><OptionList options={sortOptions} onChange={sortChangeHandler} /></Popover></Stack><Stack direction="column" spacing={1}>{myData.map((e, i) => {const valueArr = filterValue.map((e) => multiOptions[e].text.toLowerCase());if (valueArr.length === 0) return <Card padding="thin" key={i}>{e.name}</Card>if (valueArr.includes(e.type)) return <Card padding="thin" key={i}>{e.name}</Card>})}</Stack></Stack>);};render (SortFilterExample)
A search field used in combination with sort and filter acts as a filter rather than as content discovery.
const SearchTermExample = () => {const [filterValue, setFilterValue] = React.useState([]);const [sortValue, setSortValue] = React.useState(0);const [sortOpen, setSortOpen] = React.useState(false);const [filterOpen, setFilterOpen] = React.useState(false);const [searchTerm, setSearchTerm] = React.useState('');const searchChangeHandler = (e, d) => {setSearchTerm(d.value);}const filterChangeHandler = (data, checked) => {if (checked) {setFilterValue((prevState) => [...prevState, data]);} else {setFilterValue((prevState) =>[...prevState].filter((item) => item !== data));}};const sortChangeHandler = (e) => {setSortValue(e);setSortOpen(false);};const sortOptions = [{text: 'Name: A to Z',value: 0,},{text: 'Name: Z to A',value: 1,},];const multiOptions = [{text: 'Country',value: 0,},{text: 'City',value: 1,},];const arr = [{name: 'Los Angeles', type: 'city'},{name: 'Mexico', type: 'country'},{name: 'Seoul', type: 'city'},{name: 'Spain', type: 'country'},{name: 'USA', type: 'country'},];const [state, setState] = React.useState(arr);React.useEffect(() => {function filtering() {const valueArr = filterValue.map((e) => multiOptions[e].text.toLowerCase());if (valueArr.length > 0) {setState(arr.filter((e) => valueArr.includes(e.type) && e.name.toLowerCase().includes(searchTerm.toLowerCase()) ));} else {setState(arr.filter((e) => e.name.toLowerCase().includes(searchTerm.toLowerCase())))}}filtering()}, [searchTerm, filterValue]);return (<Stack direction="column" spacing={2}><Stack spacing={1}><Input size="small" icon="search" iconPosition="left" onChange={searchChangeHandler}/><Popoverel="span"direction="br"padding="s"onClickOutside={() => setFilterOpen(false)}open={filterOpen}portal // This is optionaltrigger={<FilterButtononClick={() => setFilterOpen(!filterOpen)}value={filterValue.length > 0 && filterValue.length}label="Type"expandIcon={filterOpen}/>}><OptionListmultipleoptions={multiOptions}onChange={filterChangeHandler}value={filterValue}/></Popover><Popoverel="span"direction="br"padding="s"onClickOutside={() => setSortOpen(false)}open={sortOpen}portal // This is optionaltrigger={<FilterButtononClick={() => setSortOpen(!sortOpen)}value={sortValue !== 0 && sortOptions[sortValue].text}label="Sort"expandIcon={sortOpen}/>}><OptionList options={sortOptions} onChange={sortChangeHandler} /></Popover></Stack><Stack direction="column" spacing={1}>{state.map((e, i) => <Card padding="thin" key={i}>{e.name}</Card>)}</Stack></Stack>);};render (SearchTermExample)
A selectable table should not allow sorting or filtering when an item is selected. The selection can easily be lost.
const TableExample = () => {const [filterValue, setFilterValue] = React.useState([]);const [sortValue, setSortValue] = React.useState(0);const [sortOpen, setSortOpen] = React.useState(false);const [filterOpen, setFilterOpen] = React.useState(false);const [searchTerm, setSearchTerm] = React.useState('');const arr = [{name: 'Los Angeles', type: 'city', selected: false},{name: 'Mexico', type: 'country', selected: false},{name: 'Seoul', type: 'city', selected: false},{name: 'Spain', type: 'country', selected: false},{name: 'USA', type: 'country', selected: false},];const [state, setState] = React.useState(arr);const searchChangeHandler = (e, d) => {setSearchTerm(d.value);}const filterChangeHandler = (data, checked) => {if (checked) {setFilterValue((prevState) => [...prevState, data]);} else {setFilterValue((prevState) =>[...prevState].filter((item) => item !== data));}};React.useEffect(() => {function filtering() {const valueArr = filterValue.map((e) => multiOptions[e].text.toLowerCase());if (valueArr.length > 0) {setState(arr.filter((e) => valueArr.includes(e.type) && e.name.toLowerCase().includes(searchTerm.toLowerCase()) ));} else {setState(arr.filter((e) => e.name.toLowerCase().includes(searchTerm.toLowerCase())))}}filtering()}, [searchTerm, filterValue]);const sortChangeHandler = (e) => {setSortValue(e);if (e === 0) {setState(prev => prev.sort((a, b) => a.name.localeCompare(b.name)));} else {setState(prev => prev.sort((a, b) => b.name.localeCompare(a.name)));}setSortOpen(false);};const sortOptions = [{text: 'Name: A to Z',value: 0,},{text: 'Name: Z to A',value: 1,},];const multiOptions = [{text: 'Country',value: 0,},{text: 'City',value: 1,},];const selectionChange = (event) => setState(prev => prev.map(item => item.name === event.dataItem.name? {...item, selected: !item.selected}: item));const headerSelectionChange = (event) => setState(prev => prev.map(item => ({...item, selected: event.syntheticEvent.target.checked})));return (<Stack direction="column" spacing={2}><Stack justifyContent="space-between" alignContent="center">{state.every(i => !i.selected) ? (<Stack spacing={1} style={{height: 32}}><Input size="small" icon="search" iconPosition="left" onChange={searchChangeHandler}/><Popoverel="span"direction="br"padding="s"onClickOutside={() => setFilterOpen(false)}open={filterOpen}portal // This is optionaltrigger={<FilterButtononClick={() => setFilterOpen(!filterOpen)}value={filterValue.length > 0 && filterValue.length}label="Type"expandIcon={filterOpen}/>}><OptionListmultipleoptions={multiOptions}onChange={filterChangeHandler}value={filterValue}/></Popover><Popoverel="span"direction="br"padding="s"onClickOutside={() => setSortOpen(false)}open={sortOpen}portal // This is optionaltrigger={<FilterButtononClick={() => setSortOpen(!sortOpen)}value={sortValue !== 0 && sortOptions[sortValue].text}label="Sort"expandIcon={sortOpen}/>}><OptionList options={sortOptions} onChange={sortChangeHandler} /></Popover></Stack>) : <div style={{height: 32}}>{state.filter((e) => e.selected).length} item{state.filter((e) => e.selected).length > 1 && 's'} selected</div>}<Stack><Button disabled={state.every(i => !i.selected)} onClick={() => alert("Action Triggered")}>Action</Button></Stack></Stack><Stack direction="column" spacing={1}><Tabledata={state}total={state.length}selectedField="selected"onSelectionChange={selectionChange}onHeaderSelectionChange={headerSelectionChange}><TableColumnfield="selected"width="50px"// headerSelectionValue={state.every(i => i.selected)}/><TableColumnfield="name"title="Name"/><TableColumnfield="type"title="Type"/></Table></Stack></Stack>);};render (TableExample)