DataCard
Item | Name | Description |
---|---|---|
A | <DataCard /> | The <DataCard /> contains the data for each individual card |
B | <DataCardCheckbox /> | The <Checkbox /> allows users the choice to select a <DataCard /> for performing bulk actions. <Checkbox /> is optional and can be turned off when a<DataCard /> is not meant to be selectable. |
C | <DataCardRecord /> | The <DataCardRecord /> is comprised of a heading prop that displays the row’s title and a row prop that displays the row’s data |
D | <Menu /> | <Menu /> with <IconButton /> is used when multiple actions are associated with the <DataCard /> . If there's only a single action, use <Button /> instead and relocate the action from the top right-hand corner to the bottom of the <DataCard /> . |
- NameLiana Varo
- DepartmentMarketing
- Job titleContractor
- NameHenry Cote
- DepartmentHuman Resources
- Job titleEngineer
- NameMaria Stephens
- DepartmentMarketing
- Job titleChief Editor
- NameMark Brouwer
- DepartmentAccounting and Finance
- Job titlePeople Operations
- NameTatiana Robu
- DepartmentR&D
- Job titleDesigner
- NameRuby Nguyen
- DepartmentSales
- Job titleSr. Engineer
- NameSergio Guevara
- DepartmentSales
- Job titleAccount Manager
- NameRafe Walker
- DepartmentAccounting and Finance
- Job titleInformation Security Specialist
- NameFabian Lupea
- DepartmentLegal
- Job titleOffice Operations
- NameSienna Brown
- DepartmentOperations
- Job titleCTO
import React from 'react'import {Box,Button,Checkbox,CheckboxGroup,ControlLayout,DataCard,DataCardCheckbox,DataCardList,DataCardRecord,Dialog,DialogActions,DialogBody,DialogFooter,DialogHeader,IconButton,Input,Label,Link,Menu,MenuItem,Pagination,PaginationNavigation,PaginationNavigationButton,PaginationPerPageSelectField,TextField,useDialog,useId,useMenu,} from '@gusto/workbench'import { CaretDown, More, Search } from '@gusto/workbench-icons'import { employees } from './_employees'export const Basic = () => {// default "columns" checkbox set on page loadconst defaultColumnsOptions = ['name','department','jobTitle','emailAddress',]const emailId = useId()// default filters and their selection on page loadconst defaultFilterNames = {Department: [],Title: [],}// Menu props for the actionsMenu and filterColumnsMenu in `sm` breakpointconst [actionsMenuProps, actionsMenuButtonProps] = useMenu()const [filterColumnsMenuProps, filterColumnsMenuButtonProps] = useMenu()// filter and "columns" Dialog 'open' stateconst [filtersDialogProps, filtersDialogButtonProps] = useDialog()const [columnsDialogProps, columnsDialogButtonProps] = useDialog()const [cardSelected, setCardSelected] = React.useState<{[key: number]: boolean}>({})// used to keep track of the onChange of the "columns" checkboxes in columnsDialogconst [columnsCheckboxChecked, setColumnsCheckboxChecked] = React.useState<string[]>(defaultColumnsOptions)// used to make changes to the table on "Apply" of the filters dialogconst [columnsDisplayed, setColumnsDisplayed] = React.useState<string[]>(defaultColumnsOptions,)// used to keep track of the onChange of the filter checkboxesconst [filterCheckboxChecked, setFilterCheckboxChecked] = React.useState<{[key: string]: string[]}>(defaultFilterNames)// used to make changes to the table on "Apply" of the filters dialogconst [appliedFilterCheckboxes, setAppliedFilterCheckboxes] = React.useState<{[key: string]: string[]}>(defaultFilterNames)// used to determine filter count, if filters are applied, and filters dataconst filterCount =appliedFilterCheckboxes.Department.length +appliedFilterCheckboxes.Title.lengthconst isDataFiltered =appliedFilterCheckboxes.Department.length > 0 ||appliedFilterCheckboxes.Title.length > 0const filteredEmployees = isDataFiltered? employees.filter(employee => {return (appliedFilterCheckboxes.Department.includes(employee.Department) ||appliedFilterCheckboxes.Title.includes(employee.Title))}): employeesconst [perPage, setPerPage] = React.useState(10)const [currentPage, setCurrentPage] = React.useState(1)// used to determine if the pagination buttons are disabledconst lastPage = Math.ceil(filteredEmployees.length / perPage)const isFirstPage = currentPage === 1const isLastPage = currentPage === lastPageconst paginatedEmployees = filteredEmployees.slice((currentPage - 1) * perPage,currentPage * perPage,)// used to determine if the "select all" checkbox is checked, indeterminate, or uncheckedconst cardSelectedCount = Object.values(cardSelected).filter(val => val,).lengthconst selectAllChecked = cardSelectedCount === paginatedEmployees.lengthconst selectAllIndeterminate = cardSelectedCount > 0 && !selectAllChecked// an example of keeping track of rows selectedconst toggleSelected = (id: number) => {setCardSelected(currSelected => ({...currSelected,[id]: !cardSelected[id],}))}// an example of creating the "select all" functionalityconst toggleAllRows = () => {paginatedEmployees.map(employee => employee.Digit).forEach(digit => {setCardSelected(currRowsSelected => ({...currRowsSelected,[digit]: !selectAllChecked,}))})}// an example of keeping a state management of each columns checkbox being checkedconst columnsCheckboxOnChange = (e: React.ChangeEvent<HTMLInputElement>) => {const checkboxChecked = e.target.checkedif (checkboxChecked) {setColumnsCheckboxChecked(currChecked => [...currChecked, e.target.value])} else {setColumnsCheckboxChecked(currChecked =>currChecked.filter(value => value !== e.target.value),)}}// an example of resetting columns optionsconst resetColumnsOptions = () => {setColumnsCheckboxChecked(defaultColumnsOptions)}// an example of updating the visibility of columnsconst updateColumnVisibility = () => {setColumnsDisplayed(columnsCheckboxChecked)}// an example of keeping a state management of each filter checkbox being checkedconst filterCheckboxOnChange = (e: React.ChangeEvent<HTMLInputElement>) => {// getting the group name from CheckboxGroupconst groupName = e.target.nameconst checkboxChecked = e.target.checkedif (checkboxChecked) {setFilterCheckboxChecked(currChecked => ({...currChecked,[groupName]: [...currChecked[groupName], e.target.value],}))} else {setFilterCheckboxChecked(currChecked => ({...currChecked,[groupName]: currChecked[groupName].filter(value => value !== e.target.value,),}))}}// an example of applying filters on "apply" button submit within filters Dialogconst applyFilters = () => {filtersDialogProps.onClose()setAppliedFilterCheckboxes(filterCheckboxChecked)}// an example of resetting filters within the filters Dialogconst resetFilters = () => {setFilterCheckboxChecked(defaultFilterNames)}return (<Box maxHeight={840} overflow="auto"><Box maxWidth={490} margin="auto" padding={6}><ControlLayoutsearchField={<TextFieldlabel="Search"before={<Search />}placeholder="Search"visuallyHideLabel/>}selectAllCheckbox={<Checkboxtype="checkbox"label={cardSelectedCount > 0? `${cardSelectedCount} Selected`: `Select all (${paginatedEmployees.length})`}checked={selectAllChecked}indeterminate={selectAllIndeterminate}onChange={toggleAllRows}/>}selectAllActionsMenu={selectAllChecked || selectAllIndeterminate ? (<><Button{...actionsMenuButtonProps}after={<CaretDown />}size="small"variant="primary">Actions</Button><Menu{...actionsMenuProps}aria-label="Employee information actions menu"><MenuItem>Lorem ipsum</MenuItem><MenuItem>Lorem ipsum</MenuItem></Menu></>) : null}actions={<><IconButton{...filterColumnsMenuButtonProps}title="More actions"><More /></IconButton><Menu{...filterColumnsMenuProps}aria-label="Employee information filters menu"><MenuItem {...filtersDialogButtonProps}>Filter{filterCount > 0 ? ` (${filterCount})` : null}</MenuItem><MenuItem {...columnsDialogButtonProps}>Columns</MenuItem></Menu><Dialogaria-label="Employee information filters"{...filtersDialogProps}><DialogHeader>Filters</DialogHeader><DialogBody><Box marginBottom={6}><CheckboxGroup name="Department" legend="Departments">{Array.from(new Set(employees.map(employee => employee.Department)),).map(department => {return (<Checkboxkey={`emp-type-checkbox-${department}`}value={department}label={department}checked={filterCheckboxChecked.Department.includes(department,)}onChange={filterCheckboxOnChange}/>)})}</CheckboxGroup></Box><Box marginBottom={6}><CheckboxGroup name="Title" legend="Job title">{Array.from(new Set(employees.map(employee => employee.Title)),).map(title => {return (<Checkboxkey={`emp-type-checkbox-${title}`}value={title}label={title}checked={filterCheckboxChecked.Title.includes(title,)}onChange={filterCheckboxOnChange}/>)})}</CheckboxGroup></Box></DialogBody><DialogFooter><DialogActions><Button variant="primary" onClick={applyFilters}>Apply</Button><Button onClick={resetFilters}>Clear all filters</Button></DialogActions></DialogFooter></Dialog><Dialogaria-label="Employee information visible columns"{...columnsDialogProps}><DialogHeader>Columns</DialogHeader><DialogBody><CheckboxGroupname="columnsOptions"legend="Column options"visuallyHideLabel><Checkboxvalue="name"label="Name"checked={columnsCheckboxChecked.includes('name')}onChange={columnsCheckboxOnChange}/><Checkboxvalue="department"label="Department"checked={columnsCheckboxChecked.includes('department')}onChange={columnsCheckboxOnChange}/><Checkboxvalue="jobTitle"label="Job title"checked={columnsCheckboxChecked.includes('jobTitle')}onChange={columnsCheckboxOnChange}/><Checkboxvalue="emailAddress"label="Email address"checked={columnsCheckboxChecked.includes('emailAddress')}onChange={columnsCheckboxOnChange}/></CheckboxGroup></DialogBody><DialogFooter><DialogActions><Buttonvariant="primary"onClick={() => {columnsDialogProps.onClose()updateColumnVisibility()}}>Apply</Button><Button onClick={resetColumnsOptions}>Reset</Button></DialogActions></DialogFooter></Dialog></>}/><DataCardList aria-label="Employee information">{paginatedEmployees.map(employee => {return (<DataCardaria-label={employee['Name-FL']}key={`emp-${employee.Digit}`}selected={cardSelected[employee.Digit]}><DataCardCheckboxlabel={`Select ${employee['Name-FL']}`}checked={cardSelected[employee.Digit] || false}onChange={() => toggleSelected(employee.Digit)}/>{columnsDisplayed.includes('name') ? (<DataCardRecord heading="Name"><Link href="https://gusto.com/">{employee['Name-FL']}</Link></DataCardRecord>) : null}{columnsDisplayed.includes('department') ? (<DataCardRecord heading="Department"><Link href="https://gusto.com/">{employee.Department}</Link></DataCardRecord>) : null}{columnsDisplayed.includes('jobTitle') ? (<DataCardRecord heading="Job title"><Link href="https://gusto.com/">{employee.Title}</Link></DataCardRecord>) : null}{columnsDisplayed.includes('emailAddress') ? (<DataCardRecordheading={<Label htmlFor={emailId + String(employee.Digit)}>Email Address</Label>}><InputautoComplete="email"id={emailId + String(employee.Digit)}value={employee.Email}readOnly/></DataCardRecord>) : null}</DataCard>)})}</DataCardList><Box marginTop={4}><Pagination aria-label="Employee information list" variant="floating"><PaginationPerPageSelectFieldvalue={perPage}onChange={e => setPerPage(parseInt(e.target.value, 10))}/><PaginationNavigationhasPreviousPage={!isFirstPage}hasNextPage={!isLastPage}><PaginationNavigationButtonvariant="first"onClick={() => setCurrentPage(1)}/><PaginationNavigationButtonvariant="previous"onClick={() => setCurrentPage(currentPage - 1)}/><PaginationNavigationButtonvariant="next"onClick={() => setCurrentPage(currentPage + 1)}/><PaginationNavigationButtonvariant="last"onClick={() => setCurrentPage(lastPage)}/></PaginationNavigation></Pagination></Box></Box></Box>)}
- NameHannah Arendt
- DepartmentMarketing
- Job titleAccount Manager
import React from 'react'import {DataCard,DataCardCheckbox,DataCardList,DataCardRecord,Flex,Input,Label,Link,useId,} from '@gusto/workbench'export const DataCardNoActions = ({ ...props }) => {const [cardSelected, setCardSelected] = React.useState<{[key: number]: boolean}>({})const emailId = useId()// an example of keeping track of rows selectedconst toggleSelected = (id: number) => {setCardSelected(currSelected => ({...currSelected,[id]: !cardSelected[id],}))}return (<Flex justifyContent="center"><DataCardList {...props} aria-label="Employee information"><DataCard aria-label="Hannah Aredent" selected={cardSelected[0]}><DataCardCheckboxlabel="Select Hannah Aredent"checked={cardSelected[0] || false}onChange={() => toggleSelected(0)}/><DataCardRecord heading="Name"><Link href="https://gusto.com/">Hannah Arendt</Link></DataCardRecord><DataCardRecord heading="Department"><Link href="https://gusto.com/">Marketing</Link></DataCardRecord><DataCardRecord heading="Job title"><Link href="https://gusto.com/">Account Manager</Link></DataCardRecord><DataCardRecordheading={<Label htmlFor={emailId}>Email Address</Label>}><InputautoComplete="email"id={emailId}readOnly/></DataCardRecord></DataCard></DataCardList></Flex>)}
- NameHannah Arendt
- DepartmentMarketing
- Job titleAccount Manager
import React from 'react'import {Button,DataCard,DataCardAction,DataCardCheckbox,DataCardList,DataCardRecord,Flex,Input,Label,Link,useId,} from '@gusto/workbench'export const DataCardOneAction = ({ ...props }) => {const [cardSelected, setCardSelected] = React.useState<{[key: number]: boolean}>({})const emailId = useId()// an example of keeping track of rows selectedconst toggleSelected = (id: number) => {setCardSelected(currSelected => ({...currSelected,[id]: !cardSelected[id],}))}return (<Flex justifyContent="center"><DataCardList {...props} aria-label="Employee information"><DataCard aria-label="Hannah Aredent" selected={cardSelected[0]}><DataCardCheckboxlabel="Select Hannah Aredent"checked={cardSelected[0] || false}onChange={() => toggleSelected(0)}/><DataCardRecord heading="Name"><Link href="https://gusto.com/">Hannah Arendt</Link></DataCardRecord><DataCardRecord heading="Department"><Link href="https://gusto.com/">Marketing</Link></DataCardRecord><DataCardRecord heading="Job title"><Link href="https://gusto.com/">Account Manager</Link></DataCardRecord><DataCardRecordheading={<Label htmlFor={emailId}>Email Address</Label>}><InputautoComplete="email"id={emailId}readOnly/></DataCardRecord><DataCardAction><Button variant="secondary" size="small">Skip</Button></DataCardAction></DataCard></DataCardList></Flex>)}
- NameHannah Arendt
- DepartmentMarketing
- Job titleAccount Manager
import React from 'react'import {DataCard,DataCardActionsMenu,DataCardCheckbox,DataCardList,DataCardRecord,Flex,IconButton,Input,Label,Link,Menu,MenuItem,useId,useMenu,} from '@gusto/workbench'import { More } from '@gusto/workbench-icons'export const DataCardWithMultipleActions = ({ ...props }) => {const [cardSelected, setCardSelected] = React.useState<{[key: number]: boolean}>({})const [menuProps, menuButtonProps] = useMenu()const emailId = useId()// an example of keeping track of rows selectedconst toggleSelected = (id: number) => {setCardSelected(currSelected => ({...currSelected,[id]: !cardSelected[id],}))}return (<Flex justifyContent="center"><DataCardList {...props} aria-label="Employee information"><DataCard aria-label="Hannah Aredent" selected={cardSelected[0]}><DataCardCheckboxlabel="Select Hannah Aredent"checked={cardSelected[0] || false}onChange={() => toggleSelected(0)}/><DataCardRecord heading="Name"><Link href="https://gusto.com/">Hannah Arendt</Link></DataCardRecord><DataCardRecord heading="Department"><Link href="https://gusto.com/">Marketing</Link></DataCardRecord><DataCardRecord heading="Job title"><Link href="https://gusto.com/">Account Manager</Link></DataCardRecord><DataCardRecordheading={<Label htmlFor={emailId}>Email Address</Label>}><InputautoComplete="email"id={emailId}readOnly/></DataCardRecord><DataCardActionsMenu><IconButton {...menuButtonProps} title="More actions"><More /></IconButton><Menu {...menuProps} aria-label="All menu item variants"><MenuItem>Action one</MenuItem><MenuItem>Action two</MenuItem><MenuItem>Action three</MenuItem></Menu></DataCardActionsMenu></DataCard></DataCardList></Flex>)}
Name | Type | Default | Description |
---|---|---|---|
aria-label | string | - | Set the `aria-label` attribute to the `<ul>` element. |
aria-labelledby | string | - | Set the `aria-labelledby` attribute to the `<ul>` element. |
children | ReactNode | - | The content of the component. |
selected | boolean | false | Determine if the card is currently selected. |
Name | Type | Default | Description |
---|---|---|---|
children | ReactNode | - | The content of the component. |
Name | Type | Default | Description |
---|---|---|---|
children | ReactNode | - | The content of the component. |
Name | Type | Default | Description |
---|---|---|---|
children | ReactNode | - | The content of the component. |
label Required | ReactNode | - | Label text for the associated `<label>` of the checkbox. |
Name | Type | Default | Description |
---|---|---|---|
aria-label | string | - | Set the `aria-label` attribute to the `<ul>` element. |
aria-labelledby | string | - | Set the `aria-labelledby` attribute to the `<ul>` element. |
children | ReactNode | - | The content of the component. |
Name | Type | Default | Description |
---|---|---|---|
children | ReactNode | - | The content of the component. |
heading Required | ReactNode | - | The heading for the record entry. |
The following testing snippet(s) offer suggestions for testing the component using React Testing Library with occasional help from Jest.
import {DataCard,DataCardCheckbox,DataCardList,DataCardRecord,} from '@gusto/workbench';it('tests for basic data card render and functionality', async () => {render(<DataCardList aria-label="employees"><DataCard aria-label="Hannah Ardent"><DataCardCheckboxlabel="Select Hannah Aredent"onChange={jest.fn()}/><DataCardRecord heading="Full name">Hannah Ardent</DataCardRecord><DataCardRecord heading="Hours">40</DataCardRecord></DataCard><DataCard aria-label="Isaiah Berlin"><DataCardCheckboxlabel="Select Isaiah Berlin"onChange={jest.fn()}/><DataCardRecord heading="Full name">Isaiah Berlin</DataCardRecord><DataCardRecord heading="Hours">32</DataCardRecord></DataCard></DataCardList>);/** DataCardList */// Ensure the data card list has the expected accessible name and readingconst dataCardList = screen.getByRole('list', { name: 'employees' });expect(dataCardList).toBeInTheDocument();expect(dataCardList).toHaveAccessibleName('employees');/** DataCard */// Ensure each data card has the expected accessible name and readingconst hannahCard = screen.getByRole('list', { name: 'Hannah Ardent' });expect(hannahCard).toBeInTheDocument();expect(hannahCard).toHaveAccessibleName('Hannah Ardent');const isaiahCard = screen.getByRole('list', { name: 'Isaiah Berlin' });expect(isaiahCard).toBeInTheDocument();expect(isaiahCard).toHaveAccessibleName('Isaiah Berlin');/** DataCardCheckbox */// Assert data card selectionconst hannahCheckbox = within(hannahCard).getByRole('checkbox', { name: 'Select entry for Hannah Ardent' });// Click the row selection checkboxuserEvent.click(hannahCheckbox);// Assert checkbox click expectations})