-
Notifications
You must be signed in to change notification settings - Fork 1.3k
fix: Inline editing tables #8983
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 3 commits
f929cff
5e653be
3aab946
0d989aa
1b6cf71
9ad6e91
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -1458,6 +1458,114 @@ let editableColumns: Array<Omit<ColumnProps, 'children'> & {name: string}> = [ | |||
interface EditableTableProps extends TableViewProps {} | ||||
|
||||
export const EditableTable: StoryObj<EditableTableProps> = { | ||||
render: function EditableTable(props) { | ||||
let columns = editableColumns; | ||||
let [editableItems, setEditableItems] = useState(defaultItems); | ||||
let intermediateValue = useRef<any>(null); | ||||
|
||||
let onChange = useCallback((id: Key, columnId: Key) => { | ||||
let value = intermediateValue.current; | ||||
if (value === null) { | ||||
return; | ||||
} | ||||
intermediateValue.current = null; | ||||
setEditableItems(prev => { | ||||
let newItems = prev.map(i => i.id === id && i[columnId] !== value ? {...i, [columnId]: value} : i); | ||||
return newItems; | ||||
}); | ||||
}, []); | ||||
|
||||
let onIntermediateChange = useCallback((value: any) => { | ||||
intermediateValue.current = value; | ||||
}, []); | ||||
|
||||
return ( | ||||
<div className={style({display: 'flex', flexDirection: 'column', gap: 16})}> | ||||
<TableView aria-label="Dynamic table" {...props} styles={style({width: 800, maxWidth: 'calc(100vw - 2rem)', height: 208})}> | ||||
<TableHeader columns={columns}> | ||||
{(column) => ( | ||||
<Column {...column}>{column.name}</Column> | ||||
)} | ||||
</TableHeader> | ||||
<TableBody items={editableItems}> | ||||
{item => ( | ||||
<Row id={item.id} columns={columns}> | ||||
{(column) => { | ||||
if (column.id === 'fruits') { | ||||
return ( | ||||
<EditableCell | ||||
align={column.align} | ||||
showDivider={column.showDivider} | ||||
onSubmit={() => onChange(item.id, column.id!)} | ||||
onCancel={() => {}} | ||||
isSaving={item.isSaving[column.id!]} | ||||
renderEditing={() => ( | ||||
<TextField | ||||
aria-label="Edit fruit" | ||||
autoFocus | ||||
validate={value => value.length > 0 ? null : 'Fruit name is required'} | ||||
styles={style({flexGrow: 1, flexShrink: 1, minWidth: 0})} | ||||
defaultValue={item[column.id!]} | ||||
onChange={value => onIntermediateChange(value)} /> | ||||
)}> | ||||
<div className={style({display: 'flex', alignItems: 'center', gap: 8, justifyContent: 'space-between'})}> | ||||
{item[column.id]} | ||||
<ActionButton slot="edit" aria-label="Edit fruit"> | ||||
<Edit /> | ||||
</ActionButton></div> | ||||
</EditableCell> | ||||
); | ||||
} | ||||
if (column.id === 'farmer') { | ||||
return ( | ||||
<EditableCell | ||||
align={column.align} | ||||
showDivider={column.showDivider} | ||||
onSubmit={() => onChange(item.id, column.id!)} | ||||
onCancel={() => {}} | ||||
isSaving={item.isSaving[column.id!]} | ||||
renderEditing={() => ( | ||||
<Picker | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just a nitpick, but I noticed that opening the picker here on mobile now properly expands the dropdown to grow to fit the available Table space, albiet with a slightly strange "grow" animation that only happens the first time the picker gets opened. However, I was wondering why it doesn't do that for the Async example? I was assuming it would use the width of the table as its bounds per say There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also noticed that "grow" animation, I have no idea where it's coming from. There should be a max width on the popover of the table's width,
|
||||
aria-label="Edit farmer" | ||||
autoFocus | ||||
styles={style({flexGrow: 1, flexShrink: 1, minWidth: 0})} | ||||
defaultValue={item[column.id!]} | ||||
onChange={value => onIntermediateChange(value)}> | ||||
<PickerItem textValue="Eva" id="Eva"><User /><Text>Eva</Text></PickerItem> | ||||
<PickerItem textValue="Steven" id="Steven"><User /><Text>Steven</Text></PickerItem> | ||||
<PickerItem textValue="Michael" id="Michael"><User /><Text>Michael</Text></PickerItem> | ||||
<PickerItem textValue="Sara" id="Sara"><User /><Text>Sara</Text></PickerItem> | ||||
<PickerItem textValue="Karina" id="Karina"><User /><Text>Karina</Text></PickerItem> | ||||
<PickerItem textValue="Otto" id="Otto"><User /><Text>Otto</Text></PickerItem> | ||||
<PickerItem textValue="Matt" id="Matt"><User /><Text>Matt</Text></PickerItem> | ||||
<PickerItem textValue="Emily" id="Emily"><User /><Text>Emily</Text></PickerItem> | ||||
<PickerItem textValue="Amelia" id="Amelia"><User /><Text>Amelia</Text></PickerItem> | ||||
<PickerItem textValue="Isla" id="Isla"><User /><Text>Isla</Text></PickerItem> | ||||
</Picker> | ||||
)}> | ||||
<div className={style({display: 'flex', alignItems: 'center', gap: 8, justifyContent: 'space-between'})}>{item[column.id]}<ActionButton slot="edit" aria-label="Edit fruit"><Edit /></ActionButton></div> | ||||
</EditableCell> | ||||
); | ||||
} | ||||
if (column.id === 'status') { | ||||
return ( | ||||
<Cell align={column.align} showDivider={column.showDivider}> | ||||
<StatusLight variant="informative">{item[column.id]}</StatusLight> | ||||
</Cell> | ||||
); | ||||
} | ||||
return <Cell align={column.align} showDivider={column.showDivider}>{item[column.id!]}</Cell>; | ||||
}} | ||||
</Row> | ||||
)} | ||||
</TableBody> | ||||
</TableView> | ||||
</div> | ||||
); | ||||
} | ||||
}; | ||||
|
||||
export const EditableTableWithAsyncSaving: StoryObj<EditableTableProps> = { | ||||
render: function EditableTable(props) { | ||||
let columns = editableColumns; | ||||
let [editableItems, setEditableItems] = useState(defaultItems); | ||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that I didn't write a test for this because it seems like
user.tab()
doesn't emulate this browser behavior